##// END OF EJS Templates
hgweb: prefix line id by ctx shortnode in filelog when patches are shown...
Denis Laxalde -
r31727:6be6e4be default
parent child Browse files
Show More
@@ -1,1373 +1,1374 b''
1 #
1 #
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms 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 context,
31 context,
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
134
135 if util.binary(text):
135 if util.binary(text):
136 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
136 mt = mimetypes.guess_type(f)[0] or 'application/octet-stream'
137 text = '(binary:%s)' % mt
137 text = '(binary:%s)' % mt
138
138
139 def lines():
139 def lines():
140 for lineno, t in enumerate(text.splitlines(True)):
140 for lineno, t in enumerate(text.splitlines(True)):
141 yield {"line": t,
141 yield {"line": t,
142 "lineid": "l%d" % (lineno + 1),
142 "lineid": "l%d" % (lineno + 1),
143 "linenumber": "% 6d" % (lineno + 1),
143 "linenumber": "% 6d" % (lineno + 1),
144 "parity": next(parity)}
144 "parity": next(parity)}
145
145
146 return tmpl("filerevision",
146 return tmpl("filerevision",
147 file=f,
147 file=f,
148 path=webutil.up(f),
148 path=webutil.up(f),
149 text=lines(),
149 text=lines(),
150 symrev=webutil.symrevorshortnode(req, fctx),
150 symrev=webutil.symrevorshortnode(req, fctx),
151 rename=webutil.renamelink(fctx),
151 rename=webutil.renamelink(fctx),
152 permissions=fctx.manifest().flags(f),
152 permissions=fctx.manifest().flags(f),
153 **webutil.commonentry(web.repo, fctx))
153 **webutil.commonentry(web.repo, fctx))
154
154
155 @webcommand('file')
155 @webcommand('file')
156 def file(web, req, tmpl):
156 def file(web, req, tmpl):
157 """
157 """
158 /file/{revision}[/{path}]
158 /file/{revision}[/{path}]
159 -------------------------
159 -------------------------
160
160
161 Show information about a directory or file in the repository.
161 Show information about a directory or file in the repository.
162
162
163 Info about the ``path`` given as a URL parameter will be rendered.
163 Info about the ``path`` given as a URL parameter will be rendered.
164
164
165 If ``path`` is a directory, information about the entries in that
165 If ``path`` is a directory, information about the entries in that
166 directory will be rendered. This form is equivalent to the ``manifest``
166 directory will be rendered. This form is equivalent to the ``manifest``
167 handler.
167 handler.
168
168
169 If ``path`` is a file, information about that file will be shown via
169 If ``path`` is a file, information about that file will be shown via
170 the ``filerevision`` template.
170 the ``filerevision`` template.
171
171
172 If ``path`` is not defined, information about the root directory will
172 If ``path`` is not defined, information about the root directory will
173 be rendered.
173 be rendered.
174 """
174 """
175 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
175 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
176 if not path:
176 if not path:
177 return manifest(web, req, tmpl)
177 return manifest(web, req, tmpl)
178 try:
178 try:
179 return _filerevision(web, req, tmpl, webutil.filectx(web.repo, req))
179 return _filerevision(web, req, tmpl, webutil.filectx(web.repo, req))
180 except error.LookupError as inst:
180 except error.LookupError as inst:
181 try:
181 try:
182 return manifest(web, req, tmpl)
182 return manifest(web, req, tmpl)
183 except ErrorResponse:
183 except ErrorResponse:
184 raise inst
184 raise inst
185
185
186 def _search(web, req, tmpl):
186 def _search(web, req, tmpl):
187 MODE_REVISION = 'rev'
187 MODE_REVISION = 'rev'
188 MODE_KEYWORD = 'keyword'
188 MODE_KEYWORD = 'keyword'
189 MODE_REVSET = 'revset'
189 MODE_REVSET = 'revset'
190
190
191 def revsearch(ctx):
191 def revsearch(ctx):
192 yield ctx
192 yield ctx
193
193
194 def keywordsearch(query):
194 def keywordsearch(query):
195 lower = encoding.lower
195 lower = encoding.lower
196 qw = lower(query).split()
196 qw = lower(query).split()
197
197
198 def revgen():
198 def revgen():
199 cl = web.repo.changelog
199 cl = web.repo.changelog
200 for i in xrange(len(web.repo) - 1, 0, -100):
200 for i in xrange(len(web.repo) - 1, 0, -100):
201 l = []
201 l = []
202 for j in cl.revs(max(0, i - 99), i):
202 for j in cl.revs(max(0, i - 99), i):
203 ctx = web.repo[j]
203 ctx = web.repo[j]
204 l.append(ctx)
204 l.append(ctx)
205 l.reverse()
205 l.reverse()
206 for e in l:
206 for e in l:
207 yield e
207 yield e
208
208
209 for ctx in revgen():
209 for ctx in revgen():
210 miss = 0
210 miss = 0
211 for q in qw:
211 for q in qw:
212 if not (q in lower(ctx.user()) or
212 if not (q in lower(ctx.user()) or
213 q in lower(ctx.description()) or
213 q in lower(ctx.description()) or
214 q in lower(" ".join(ctx.files()))):
214 q in lower(" ".join(ctx.files()))):
215 miss = 1
215 miss = 1
216 break
216 break
217 if miss:
217 if miss:
218 continue
218 continue
219
219
220 yield ctx
220 yield ctx
221
221
222 def revsetsearch(revs):
222 def revsetsearch(revs):
223 for r in revs:
223 for r in revs:
224 yield web.repo[r]
224 yield web.repo[r]
225
225
226 searchfuncs = {
226 searchfuncs = {
227 MODE_REVISION: (revsearch, 'exact revision search'),
227 MODE_REVISION: (revsearch, 'exact revision search'),
228 MODE_KEYWORD: (keywordsearch, 'literal keyword search'),
228 MODE_KEYWORD: (keywordsearch, 'literal keyword search'),
229 MODE_REVSET: (revsetsearch, 'revset expression search'),
229 MODE_REVSET: (revsetsearch, 'revset expression search'),
230 }
230 }
231
231
232 def getsearchmode(query):
232 def getsearchmode(query):
233 try:
233 try:
234 ctx = web.repo[query]
234 ctx = web.repo[query]
235 except (error.RepoError, error.LookupError):
235 except (error.RepoError, error.LookupError):
236 # query is not an exact revision pointer, need to
236 # query is not an exact revision pointer, need to
237 # decide if it's a revset expression or keywords
237 # decide if it's a revset expression or keywords
238 pass
238 pass
239 else:
239 else:
240 return MODE_REVISION, ctx
240 return MODE_REVISION, ctx
241
241
242 revdef = 'reverse(%s)' % query
242 revdef = 'reverse(%s)' % query
243 try:
243 try:
244 tree = revsetlang.parse(revdef)
244 tree = revsetlang.parse(revdef)
245 except error.ParseError:
245 except error.ParseError:
246 # can't parse to a revset tree
246 # can't parse to a revset tree
247 return MODE_KEYWORD, query
247 return MODE_KEYWORD, query
248
248
249 if revsetlang.depth(tree) <= 2:
249 if revsetlang.depth(tree) <= 2:
250 # no revset syntax used
250 # no revset syntax used
251 return MODE_KEYWORD, query
251 return MODE_KEYWORD, query
252
252
253 if any((token, (value or '')[:3]) == ('string', 're:')
253 if any((token, (value or '')[:3]) == ('string', 're:')
254 for token, value, pos in revsetlang.tokenize(revdef)):
254 for token, value, pos in revsetlang.tokenize(revdef)):
255 return MODE_KEYWORD, query
255 return MODE_KEYWORD, query
256
256
257 funcsused = revsetlang.funcsused(tree)
257 funcsused = revsetlang.funcsused(tree)
258 if not funcsused.issubset(revset.safesymbols):
258 if not funcsused.issubset(revset.safesymbols):
259 return MODE_KEYWORD, query
259 return MODE_KEYWORD, query
260
260
261 mfunc = revset.match(web.repo.ui, revdef)
261 mfunc = revset.match(web.repo.ui, revdef)
262 try:
262 try:
263 revs = mfunc(web.repo)
263 revs = mfunc(web.repo)
264 return MODE_REVSET, revs
264 return MODE_REVSET, revs
265 # ParseError: wrongly placed tokens, wrongs arguments, etc
265 # ParseError: wrongly placed tokens, wrongs arguments, etc
266 # RepoLookupError: no such revision, e.g. in 'revision:'
266 # RepoLookupError: no such revision, e.g. in 'revision:'
267 # Abort: bookmark/tag not exists
267 # Abort: bookmark/tag not exists
268 # LookupError: ambiguous identifier, e.g. in '(bc)' on a large repo
268 # LookupError: ambiguous identifier, e.g. in '(bc)' on a large repo
269 except (error.ParseError, error.RepoLookupError, error.Abort,
269 except (error.ParseError, error.RepoLookupError, error.Abort,
270 LookupError):
270 LookupError):
271 return MODE_KEYWORD, query
271 return MODE_KEYWORD, query
272
272
273 def changelist(**map):
273 def changelist(**map):
274 count = 0
274 count = 0
275
275
276 for ctx in searchfunc[0](funcarg):
276 for ctx in searchfunc[0](funcarg):
277 count += 1
277 count += 1
278 n = ctx.node()
278 n = ctx.node()
279 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
279 showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
280 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
280 files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
281
281
282 yield tmpl('searchentry',
282 yield tmpl('searchentry',
283 parity=next(parity),
283 parity=next(parity),
284 changelogtag=showtags,
284 changelogtag=showtags,
285 files=files,
285 files=files,
286 **webutil.commonentry(web.repo, ctx))
286 **webutil.commonentry(web.repo, ctx))
287
287
288 if count >= revcount:
288 if count >= revcount:
289 break
289 break
290
290
291 query = req.form['rev'][0]
291 query = req.form['rev'][0]
292 revcount = web.maxchanges
292 revcount = web.maxchanges
293 if 'revcount' in req.form:
293 if 'revcount' in req.form:
294 try:
294 try:
295 revcount = int(req.form.get('revcount', [revcount])[0])
295 revcount = int(req.form.get('revcount', [revcount])[0])
296 revcount = max(revcount, 1)
296 revcount = max(revcount, 1)
297 tmpl.defaults['sessionvars']['revcount'] = revcount
297 tmpl.defaults['sessionvars']['revcount'] = revcount
298 except ValueError:
298 except ValueError:
299 pass
299 pass
300
300
301 lessvars = copy.copy(tmpl.defaults['sessionvars'])
301 lessvars = copy.copy(tmpl.defaults['sessionvars'])
302 lessvars['revcount'] = max(revcount / 2, 1)
302 lessvars['revcount'] = max(revcount / 2, 1)
303 lessvars['rev'] = query
303 lessvars['rev'] = query
304 morevars = copy.copy(tmpl.defaults['sessionvars'])
304 morevars = copy.copy(tmpl.defaults['sessionvars'])
305 morevars['revcount'] = revcount * 2
305 morevars['revcount'] = revcount * 2
306 morevars['rev'] = query
306 morevars['rev'] = query
307
307
308 mode, funcarg = getsearchmode(query)
308 mode, funcarg = getsearchmode(query)
309
309
310 if 'forcekw' in req.form:
310 if 'forcekw' in req.form:
311 showforcekw = ''
311 showforcekw = ''
312 showunforcekw = searchfuncs[mode][1]
312 showunforcekw = searchfuncs[mode][1]
313 mode = MODE_KEYWORD
313 mode = MODE_KEYWORD
314 funcarg = query
314 funcarg = query
315 else:
315 else:
316 if mode != MODE_KEYWORD:
316 if mode != MODE_KEYWORD:
317 showforcekw = searchfuncs[MODE_KEYWORD][1]
317 showforcekw = searchfuncs[MODE_KEYWORD][1]
318 else:
318 else:
319 showforcekw = ''
319 showforcekw = ''
320 showunforcekw = ''
320 showunforcekw = ''
321
321
322 searchfunc = searchfuncs[mode]
322 searchfunc = searchfuncs[mode]
323
323
324 tip = web.repo['tip']
324 tip = web.repo['tip']
325 parity = paritygen(web.stripecount)
325 parity = paritygen(web.stripecount)
326
326
327 return tmpl('search', query=query, node=tip.hex(), symrev='tip',
327 return tmpl('search', query=query, node=tip.hex(), symrev='tip',
328 entries=changelist, archives=web.archivelist("tip"),
328 entries=changelist, archives=web.archivelist("tip"),
329 morevars=morevars, lessvars=lessvars,
329 morevars=morevars, lessvars=lessvars,
330 modedesc=searchfunc[1],
330 modedesc=searchfunc[1],
331 showforcekw=showforcekw, showunforcekw=showunforcekw)
331 showforcekw=showforcekw, showunforcekw=showunforcekw)
332
332
333 @webcommand('changelog')
333 @webcommand('changelog')
334 def changelog(web, req, tmpl, shortlog=False):
334 def changelog(web, req, tmpl, shortlog=False):
335 """
335 """
336 /changelog[/{revision}]
336 /changelog[/{revision}]
337 -----------------------
337 -----------------------
338
338
339 Show information about multiple changesets.
339 Show information about multiple changesets.
340
340
341 If the optional ``revision`` URL argument is absent, information about
341 If the optional ``revision`` URL argument is absent, information about
342 all changesets starting at ``tip`` will be rendered. If the ``revision``
342 all changesets starting at ``tip`` will be rendered. If the ``revision``
343 argument is present, changesets will be shown starting from the specified
343 argument is present, changesets will be shown starting from the specified
344 revision.
344 revision.
345
345
346 If ``revision`` is absent, the ``rev`` query string argument may be
346 If ``revision`` is absent, the ``rev`` query string argument may be
347 defined. This will perform a search for changesets.
347 defined. This will perform a search for changesets.
348
348
349 The argument for ``rev`` can be a single revision, a revision set,
349 The argument for ``rev`` can be a single revision, a revision set,
350 or a literal keyword to search for in changeset data (equivalent to
350 or a literal keyword to search for in changeset data (equivalent to
351 :hg:`log -k`).
351 :hg:`log -k`).
352
352
353 The ``revcount`` query string argument defines the maximum numbers of
353 The ``revcount`` query string argument defines the maximum numbers of
354 changesets to render.
354 changesets to render.
355
355
356 For non-searches, the ``changelog`` template will be rendered.
356 For non-searches, the ``changelog`` template will be rendered.
357 """
357 """
358
358
359 query = ''
359 query = ''
360 if 'node' in req.form:
360 if 'node' in req.form:
361 ctx = webutil.changectx(web.repo, req)
361 ctx = webutil.changectx(web.repo, req)
362 symrev = webutil.symrevorshortnode(req, ctx)
362 symrev = webutil.symrevorshortnode(req, ctx)
363 elif 'rev' in req.form:
363 elif 'rev' in req.form:
364 return _search(web, req, tmpl)
364 return _search(web, req, tmpl)
365 else:
365 else:
366 ctx = web.repo['tip']
366 ctx = web.repo['tip']
367 symrev = 'tip'
367 symrev = 'tip'
368
368
369 def changelist():
369 def changelist():
370 revs = []
370 revs = []
371 if pos != -1:
371 if pos != -1:
372 revs = web.repo.changelog.revs(pos, 0)
372 revs = web.repo.changelog.revs(pos, 0)
373 curcount = 0
373 curcount = 0
374 for rev in revs:
374 for rev in revs:
375 curcount += 1
375 curcount += 1
376 if curcount > revcount + 1:
376 if curcount > revcount + 1:
377 break
377 break
378
378
379 entry = webutil.changelistentry(web, web.repo[rev], tmpl)
379 entry = webutil.changelistentry(web, web.repo[rev], tmpl)
380 entry['parity'] = next(parity)
380 entry['parity'] = next(parity)
381 yield entry
381 yield entry
382
382
383 if shortlog:
383 if shortlog:
384 revcount = web.maxshortchanges
384 revcount = web.maxshortchanges
385 else:
385 else:
386 revcount = web.maxchanges
386 revcount = web.maxchanges
387
387
388 if 'revcount' in req.form:
388 if 'revcount' in req.form:
389 try:
389 try:
390 revcount = int(req.form.get('revcount', [revcount])[0])
390 revcount = int(req.form.get('revcount', [revcount])[0])
391 revcount = max(revcount, 1)
391 revcount = max(revcount, 1)
392 tmpl.defaults['sessionvars']['revcount'] = revcount
392 tmpl.defaults['sessionvars']['revcount'] = revcount
393 except ValueError:
393 except ValueError:
394 pass
394 pass
395
395
396 lessvars = copy.copy(tmpl.defaults['sessionvars'])
396 lessvars = copy.copy(tmpl.defaults['sessionvars'])
397 lessvars['revcount'] = max(revcount / 2, 1)
397 lessvars['revcount'] = max(revcount / 2, 1)
398 morevars = copy.copy(tmpl.defaults['sessionvars'])
398 morevars = copy.copy(tmpl.defaults['sessionvars'])
399 morevars['revcount'] = revcount * 2
399 morevars['revcount'] = revcount * 2
400
400
401 count = len(web.repo)
401 count = len(web.repo)
402 pos = ctx.rev()
402 pos = ctx.rev()
403 parity = paritygen(web.stripecount)
403 parity = paritygen(web.stripecount)
404
404
405 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
405 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
406
406
407 entries = list(changelist())
407 entries = list(changelist())
408 latestentry = entries[:1]
408 latestentry = entries[:1]
409 if len(entries) > revcount:
409 if len(entries) > revcount:
410 nextentry = entries[-1:]
410 nextentry = entries[-1:]
411 entries = entries[:-1]
411 entries = entries[:-1]
412 else:
412 else:
413 nextentry = []
413 nextentry = []
414
414
415 return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
415 return tmpl(shortlog and 'shortlog' or 'changelog', changenav=changenav,
416 node=ctx.hex(), rev=pos, symrev=symrev, changesets=count,
416 node=ctx.hex(), rev=pos, symrev=symrev, changesets=count,
417 entries=entries,
417 entries=entries,
418 latestentry=latestentry, nextentry=nextentry,
418 latestentry=latestentry, nextentry=nextentry,
419 archives=web.archivelist("tip"), revcount=revcount,
419 archives=web.archivelist("tip"), revcount=revcount,
420 morevars=morevars, lessvars=lessvars, query=query)
420 morevars=morevars, lessvars=lessvars, query=query)
421
421
422 @webcommand('shortlog')
422 @webcommand('shortlog')
423 def shortlog(web, req, tmpl):
423 def shortlog(web, req, tmpl):
424 """
424 """
425 /shortlog
425 /shortlog
426 ---------
426 ---------
427
427
428 Show basic information about a set of changesets.
428 Show basic information about a set of changesets.
429
429
430 This accepts the same parameters as the ``changelog`` handler. The only
430 This accepts the same parameters as the ``changelog`` handler. The only
431 difference is the ``shortlog`` template will be rendered instead of the
431 difference is the ``shortlog`` template will be rendered instead of the
432 ``changelog`` template.
432 ``changelog`` template.
433 """
433 """
434 return changelog(web, req, tmpl, shortlog=True)
434 return changelog(web, req, tmpl, shortlog=True)
435
435
436 @webcommand('changeset')
436 @webcommand('changeset')
437 def changeset(web, req, tmpl):
437 def changeset(web, req, tmpl):
438 """
438 """
439 /changeset[/{revision}]
439 /changeset[/{revision}]
440 -----------------------
440 -----------------------
441
441
442 Show information about a single changeset.
442 Show information about a single changeset.
443
443
444 A URL path argument is the changeset identifier to show. See ``hg help
444 A URL path argument is the changeset identifier to show. See ``hg help
445 revisions`` for possible values. If not defined, the ``tip`` changeset
445 revisions`` for possible values. If not defined, the ``tip`` changeset
446 will be shown.
446 will be shown.
447
447
448 The ``changeset`` template is rendered. Contents of the ``changesettag``,
448 The ``changeset`` template is rendered. Contents of the ``changesettag``,
449 ``changesetbookmark``, ``filenodelink``, ``filenolink``, and the many
449 ``changesetbookmark``, ``filenodelink``, ``filenolink``, and the many
450 templates related to diffs may all be used to produce the output.
450 templates related to diffs may all be used to produce the output.
451 """
451 """
452 ctx = webutil.changectx(web.repo, req)
452 ctx = webutil.changectx(web.repo, req)
453
453
454 return tmpl('changeset', **webutil.changesetentry(web, req, tmpl, ctx))
454 return tmpl('changeset', **webutil.changesetentry(web, req, tmpl, ctx))
455
455
456 rev = webcommand('rev')(changeset)
456 rev = webcommand('rev')(changeset)
457
457
458 def decodepath(path):
458 def decodepath(path):
459 """Hook for mapping a path in the repository to a path in the
459 """Hook for mapping a path in the repository to a path in the
460 working copy.
460 working copy.
461
461
462 Extensions (e.g., largefiles) can override this to remap files in
462 Extensions (e.g., largefiles) can override this to remap files in
463 the virtual file system presented by the manifest command below."""
463 the virtual file system presented by the manifest command below."""
464 return path
464 return path
465
465
466 @webcommand('manifest')
466 @webcommand('manifest')
467 def manifest(web, req, tmpl):
467 def manifest(web, req, tmpl):
468 """
468 """
469 /manifest[/{revision}[/{path}]]
469 /manifest[/{revision}[/{path}]]
470 -------------------------------
470 -------------------------------
471
471
472 Show information about a directory.
472 Show information about a directory.
473
473
474 If the URL path arguments are omitted, information about the root
474 If the URL path arguments are omitted, information about the root
475 directory for the ``tip`` changeset will be shown.
475 directory for the ``tip`` changeset will be shown.
476
476
477 Because this handler can only show information for directories, it
477 Because this handler can only show information for directories, it
478 is recommended to use the ``file`` handler instead, as it can handle both
478 is recommended to use the ``file`` handler instead, as it can handle both
479 directories and files.
479 directories and files.
480
480
481 The ``manifest`` template will be rendered for this handler.
481 The ``manifest`` template will be rendered for this handler.
482 """
482 """
483 if 'node' in req.form:
483 if 'node' in req.form:
484 ctx = webutil.changectx(web.repo, req)
484 ctx = webutil.changectx(web.repo, req)
485 symrev = webutil.symrevorshortnode(req, ctx)
485 symrev = webutil.symrevorshortnode(req, ctx)
486 else:
486 else:
487 ctx = web.repo['tip']
487 ctx = web.repo['tip']
488 symrev = 'tip'
488 symrev = 'tip'
489 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
489 path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
490 mf = ctx.manifest()
490 mf = ctx.manifest()
491 node = ctx.node()
491 node = ctx.node()
492
492
493 files = {}
493 files = {}
494 dirs = {}
494 dirs = {}
495 parity = paritygen(web.stripecount)
495 parity = paritygen(web.stripecount)
496
496
497 if path and path[-1] != "/":
497 if path and path[-1] != "/":
498 path += "/"
498 path += "/"
499 l = len(path)
499 l = len(path)
500 abspath = "/" + path
500 abspath = "/" + path
501
501
502 for full, n in mf.iteritems():
502 for full, n in mf.iteritems():
503 # the virtual path (working copy path) used for the full
503 # the virtual path (working copy path) used for the full
504 # (repository) path
504 # (repository) path
505 f = decodepath(full)
505 f = decodepath(full)
506
506
507 if f[:l] != path:
507 if f[:l] != path:
508 continue
508 continue
509 remain = f[l:]
509 remain = f[l:]
510 elements = remain.split('/')
510 elements = remain.split('/')
511 if len(elements) == 1:
511 if len(elements) == 1:
512 files[remain] = full
512 files[remain] = full
513 else:
513 else:
514 h = dirs # need to retain ref to dirs (root)
514 h = dirs # need to retain ref to dirs (root)
515 for elem in elements[0:-1]:
515 for elem in elements[0:-1]:
516 if elem not in h:
516 if elem not in h:
517 h[elem] = {}
517 h[elem] = {}
518 h = h[elem]
518 h = h[elem]
519 if len(h) > 1:
519 if len(h) > 1:
520 break
520 break
521 h[None] = None # denotes files present
521 h[None] = None # denotes files present
522
522
523 if mf and not files and not dirs:
523 if mf and not files and not dirs:
524 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
524 raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path)
525
525
526 def filelist(**map):
526 def filelist(**map):
527 for f in sorted(files):
527 for f in sorted(files):
528 full = files[f]
528 full = files[f]
529
529
530 fctx = ctx.filectx(full)
530 fctx = ctx.filectx(full)
531 yield {"file": full,
531 yield {"file": full,
532 "parity": next(parity),
532 "parity": next(parity),
533 "basename": f,
533 "basename": f,
534 "date": fctx.date(),
534 "date": fctx.date(),
535 "size": fctx.size(),
535 "size": fctx.size(),
536 "permissions": mf.flags(full)}
536 "permissions": mf.flags(full)}
537
537
538 def dirlist(**map):
538 def dirlist(**map):
539 for d in sorted(dirs):
539 for d in sorted(dirs):
540
540
541 emptydirs = []
541 emptydirs = []
542 h = dirs[d]
542 h = dirs[d]
543 while isinstance(h, dict) and len(h) == 1:
543 while isinstance(h, dict) and len(h) == 1:
544 k, v = h.items()[0]
544 k, v = h.items()[0]
545 if v:
545 if v:
546 emptydirs.append(k)
546 emptydirs.append(k)
547 h = v
547 h = v
548
548
549 path = "%s%s" % (abspath, d)
549 path = "%s%s" % (abspath, d)
550 yield {"parity": next(parity),
550 yield {"parity": next(parity),
551 "path": path,
551 "path": path,
552 "emptydirs": "/".join(emptydirs),
552 "emptydirs": "/".join(emptydirs),
553 "basename": d}
553 "basename": d}
554
554
555 return tmpl("manifest",
555 return tmpl("manifest",
556 symrev=symrev,
556 symrev=symrev,
557 path=abspath,
557 path=abspath,
558 up=webutil.up(abspath),
558 up=webutil.up(abspath),
559 upparity=next(parity),
559 upparity=next(parity),
560 fentries=filelist,
560 fentries=filelist,
561 dentries=dirlist,
561 dentries=dirlist,
562 archives=web.archivelist(hex(node)),
562 archives=web.archivelist(hex(node)),
563 **webutil.commonentry(web.repo, ctx))
563 **webutil.commonentry(web.repo, ctx))
564
564
565 @webcommand('tags')
565 @webcommand('tags')
566 def tags(web, req, tmpl):
566 def tags(web, req, tmpl):
567 """
567 """
568 /tags
568 /tags
569 -----
569 -----
570
570
571 Show information about tags.
571 Show information about tags.
572
572
573 No arguments are accepted.
573 No arguments are accepted.
574
574
575 The ``tags`` template is rendered.
575 The ``tags`` template is rendered.
576 """
576 """
577 i = list(reversed(web.repo.tagslist()))
577 i = list(reversed(web.repo.tagslist()))
578 parity = paritygen(web.stripecount)
578 parity = paritygen(web.stripecount)
579
579
580 def entries(notip, latestonly, **map):
580 def entries(notip, latestonly, **map):
581 t = i
581 t = i
582 if notip:
582 if notip:
583 t = [(k, n) for k, n in i if k != "tip"]
583 t = [(k, n) for k, n in i if k != "tip"]
584 if latestonly:
584 if latestonly:
585 t = t[:1]
585 t = t[:1]
586 for k, n in t:
586 for k, n in t:
587 yield {"parity": next(parity),
587 yield {"parity": next(parity),
588 "tag": k,
588 "tag": k,
589 "date": web.repo[n].date(),
589 "date": web.repo[n].date(),
590 "node": hex(n)}
590 "node": hex(n)}
591
591
592 return tmpl("tags",
592 return tmpl("tags",
593 node=hex(web.repo.changelog.tip()),
593 node=hex(web.repo.changelog.tip()),
594 entries=lambda **x: entries(False, False, **x),
594 entries=lambda **x: entries(False, False, **x),
595 entriesnotip=lambda **x: entries(True, False, **x),
595 entriesnotip=lambda **x: entries(True, False, **x),
596 latestentry=lambda **x: entries(True, True, **x))
596 latestentry=lambda **x: entries(True, True, **x))
597
597
598 @webcommand('bookmarks')
598 @webcommand('bookmarks')
599 def bookmarks(web, req, tmpl):
599 def bookmarks(web, req, tmpl):
600 """
600 """
601 /bookmarks
601 /bookmarks
602 ----------
602 ----------
603
603
604 Show information about bookmarks.
604 Show information about bookmarks.
605
605
606 No arguments are accepted.
606 No arguments are accepted.
607
607
608 The ``bookmarks`` template is rendered.
608 The ``bookmarks`` template is rendered.
609 """
609 """
610 i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
610 i = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
611 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
611 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
612 i = sorted(i, key=sortkey, reverse=True)
612 i = sorted(i, key=sortkey, reverse=True)
613 parity = paritygen(web.stripecount)
613 parity = paritygen(web.stripecount)
614
614
615 def entries(latestonly, **map):
615 def entries(latestonly, **map):
616 t = i
616 t = i
617 if latestonly:
617 if latestonly:
618 t = i[:1]
618 t = i[:1]
619 for k, n in t:
619 for k, n in t:
620 yield {"parity": next(parity),
620 yield {"parity": next(parity),
621 "bookmark": k,
621 "bookmark": k,
622 "date": web.repo[n].date(),
622 "date": web.repo[n].date(),
623 "node": hex(n)}
623 "node": hex(n)}
624
624
625 if i:
625 if i:
626 latestrev = i[0][1]
626 latestrev = i[0][1]
627 else:
627 else:
628 latestrev = -1
628 latestrev = -1
629
629
630 return tmpl("bookmarks",
630 return tmpl("bookmarks",
631 node=hex(web.repo.changelog.tip()),
631 node=hex(web.repo.changelog.tip()),
632 lastchange=[{"date": web.repo[latestrev].date()}],
632 lastchange=[{"date": web.repo[latestrev].date()}],
633 entries=lambda **x: entries(latestonly=False, **x),
633 entries=lambda **x: entries(latestonly=False, **x),
634 latestentry=lambda **x: entries(latestonly=True, **x))
634 latestentry=lambda **x: entries(latestonly=True, **x))
635
635
636 @webcommand('branches')
636 @webcommand('branches')
637 def branches(web, req, tmpl):
637 def branches(web, req, tmpl):
638 """
638 """
639 /branches
639 /branches
640 ---------
640 ---------
641
641
642 Show information about branches.
642 Show information about branches.
643
643
644 All known branches are contained in the output, even closed branches.
644 All known branches are contained in the output, even closed branches.
645
645
646 No arguments are accepted.
646 No arguments are accepted.
647
647
648 The ``branches`` template is rendered.
648 The ``branches`` template is rendered.
649 """
649 """
650 entries = webutil.branchentries(web.repo, web.stripecount)
650 entries = webutil.branchentries(web.repo, web.stripecount)
651 latestentry = webutil.branchentries(web.repo, web.stripecount, 1)
651 latestentry = webutil.branchentries(web.repo, web.stripecount, 1)
652 return tmpl('branches', node=hex(web.repo.changelog.tip()),
652 return tmpl('branches', node=hex(web.repo.changelog.tip()),
653 entries=entries, latestentry=latestentry)
653 entries=entries, latestentry=latestentry)
654
654
655 @webcommand('summary')
655 @webcommand('summary')
656 def summary(web, req, tmpl):
656 def summary(web, req, tmpl):
657 """
657 """
658 /summary
658 /summary
659 --------
659 --------
660
660
661 Show a summary of repository state.
661 Show a summary of repository state.
662
662
663 Information about the latest changesets, bookmarks, tags, and branches
663 Information about the latest changesets, bookmarks, tags, and branches
664 is captured by this handler.
664 is captured by this handler.
665
665
666 The ``summary`` template is rendered.
666 The ``summary`` template is rendered.
667 """
667 """
668 i = reversed(web.repo.tagslist())
668 i = reversed(web.repo.tagslist())
669
669
670 def tagentries(**map):
670 def tagentries(**map):
671 parity = paritygen(web.stripecount)
671 parity = paritygen(web.stripecount)
672 count = 0
672 count = 0
673 for k, n in i:
673 for k, n in i:
674 if k == "tip": # skip tip
674 if k == "tip": # skip tip
675 continue
675 continue
676
676
677 count += 1
677 count += 1
678 if count > 10: # limit to 10 tags
678 if count > 10: # limit to 10 tags
679 break
679 break
680
680
681 yield tmpl("tagentry",
681 yield tmpl("tagentry",
682 parity=next(parity),
682 parity=next(parity),
683 tag=k,
683 tag=k,
684 node=hex(n),
684 node=hex(n),
685 date=web.repo[n].date())
685 date=web.repo[n].date())
686
686
687 def bookmarks(**map):
687 def bookmarks(**map):
688 parity = paritygen(web.stripecount)
688 parity = paritygen(web.stripecount)
689 marks = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
689 marks = [b for b in web.repo._bookmarks.items() if b[1] in web.repo]
690 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
690 sortkey = lambda b: (web.repo[b[1]].rev(), b[0])
691 marks = sorted(marks, key=sortkey, reverse=True)
691 marks = sorted(marks, key=sortkey, reverse=True)
692 for k, n in marks[:10]: # limit to 10 bookmarks
692 for k, n in marks[:10]: # limit to 10 bookmarks
693 yield {'parity': next(parity),
693 yield {'parity': next(parity),
694 'bookmark': k,
694 'bookmark': k,
695 'date': web.repo[n].date(),
695 'date': web.repo[n].date(),
696 'node': hex(n)}
696 'node': hex(n)}
697
697
698 def changelist(**map):
698 def changelist(**map):
699 parity = paritygen(web.stripecount, offset=start - end)
699 parity = paritygen(web.stripecount, offset=start - end)
700 l = [] # build a list in forward order for efficiency
700 l = [] # build a list in forward order for efficiency
701 revs = []
701 revs = []
702 if start < end:
702 if start < end:
703 revs = web.repo.changelog.revs(start, end - 1)
703 revs = web.repo.changelog.revs(start, end - 1)
704 for i in revs:
704 for i in revs:
705 ctx = web.repo[i]
705 ctx = web.repo[i]
706
706
707 l.append(tmpl(
707 l.append(tmpl(
708 'shortlogentry',
708 'shortlogentry',
709 parity=next(parity),
709 parity=next(parity),
710 **webutil.commonentry(web.repo, ctx)))
710 **webutil.commonentry(web.repo, ctx)))
711
711
712 for entry in reversed(l):
712 for entry in reversed(l):
713 yield entry
713 yield entry
714
714
715 tip = web.repo['tip']
715 tip = web.repo['tip']
716 count = len(web.repo)
716 count = len(web.repo)
717 start = max(0, count - web.maxchanges)
717 start = max(0, count - web.maxchanges)
718 end = min(count, start + web.maxchanges)
718 end = min(count, start + web.maxchanges)
719
719
720 return tmpl("summary",
720 return tmpl("summary",
721 desc=web.config("web", "description", "unknown"),
721 desc=web.config("web", "description", "unknown"),
722 owner=get_contact(web.config) or "unknown",
722 owner=get_contact(web.config) or "unknown",
723 lastchange=tip.date(),
723 lastchange=tip.date(),
724 tags=tagentries,
724 tags=tagentries,
725 bookmarks=bookmarks,
725 bookmarks=bookmarks,
726 branches=webutil.branchentries(web.repo, web.stripecount, 10),
726 branches=webutil.branchentries(web.repo, web.stripecount, 10),
727 shortlog=changelist,
727 shortlog=changelist,
728 node=tip.hex(),
728 node=tip.hex(),
729 symrev='tip',
729 symrev='tip',
730 archives=web.archivelist("tip"),
730 archives=web.archivelist("tip"),
731 labels=web.configlist('web', 'labels'))
731 labels=web.configlist('web', 'labels'))
732
732
733 @webcommand('filediff')
733 @webcommand('filediff')
734 def filediff(web, req, tmpl):
734 def filediff(web, req, tmpl):
735 """
735 """
736 /diff/{revision}/{path}
736 /diff/{revision}/{path}
737 -----------------------
737 -----------------------
738
738
739 Show how a file changed in a particular commit.
739 Show how a file changed in a particular commit.
740
740
741 The ``filediff`` template is rendered.
741 The ``filediff`` template is rendered.
742
742
743 This handler is registered under both the ``/diff`` and ``/filediff``
743 This handler is registered under both the ``/diff`` and ``/filediff``
744 paths. ``/diff`` is used in modern code.
744 paths. ``/diff`` is used in modern code.
745 """
745 """
746 fctx, ctx = None, None
746 fctx, ctx = None, None
747 try:
747 try:
748 fctx = webutil.filectx(web.repo, req)
748 fctx = webutil.filectx(web.repo, req)
749 except LookupError:
749 except LookupError:
750 ctx = webutil.changectx(web.repo, req)
750 ctx = webutil.changectx(web.repo, req)
751 path = webutil.cleanpath(web.repo, req.form['file'][0])
751 path = webutil.cleanpath(web.repo, req.form['file'][0])
752 if path not in ctx.files():
752 if path not in ctx.files():
753 raise
753 raise
754
754
755 if fctx is not None:
755 if fctx is not None:
756 path = fctx.path()
756 path = fctx.path()
757 ctx = fctx.changectx()
757 ctx = fctx.changectx()
758 basectx = ctx.p1()
758 basectx = ctx.p1()
759
759
760 style = web.config('web', 'style', 'paper')
760 style = web.config('web', 'style', 'paper')
761 if 'style' in req.form:
761 if 'style' in req.form:
762 style = req.form['style'][0]
762 style = req.form['style'][0]
763
763
764 diffs = webutil.diffs(web, tmpl, ctx, basectx, [path], style)
764 diffs = webutil.diffs(web, tmpl, ctx, basectx, [path], style)
765 if fctx is not None:
765 if fctx is not None:
766 rename = webutil.renamelink(fctx)
766 rename = webutil.renamelink(fctx)
767 ctx = fctx
767 ctx = fctx
768 else:
768 else:
769 rename = []
769 rename = []
770 ctx = ctx
770 ctx = ctx
771 return tmpl("filediff",
771 return tmpl("filediff",
772 file=path,
772 file=path,
773 symrev=webutil.symrevorshortnode(req, ctx),
773 symrev=webutil.symrevorshortnode(req, ctx),
774 rename=rename,
774 rename=rename,
775 diff=diffs,
775 diff=diffs,
776 **webutil.commonentry(web.repo, ctx))
776 **webutil.commonentry(web.repo, ctx))
777
777
778 diff = webcommand('diff')(filediff)
778 diff = webcommand('diff')(filediff)
779
779
780 @webcommand('comparison')
780 @webcommand('comparison')
781 def comparison(web, req, tmpl):
781 def comparison(web, req, tmpl):
782 """
782 """
783 /comparison/{revision}/{path}
783 /comparison/{revision}/{path}
784 -----------------------------
784 -----------------------------
785
785
786 Show a comparison between the old and new versions of a file from changes
786 Show a comparison between the old and new versions of a file from changes
787 made on a particular revision.
787 made on a particular revision.
788
788
789 This is similar to the ``diff`` handler. However, this form features
789 This is similar to the ``diff`` handler. However, this form features
790 a split or side-by-side diff rather than a unified diff.
790 a split or side-by-side diff rather than a unified diff.
791
791
792 The ``context`` query string argument can be used to control the lines of
792 The ``context`` query string argument can be used to control the lines of
793 context in the diff.
793 context in the diff.
794
794
795 The ``filecomparison`` template is rendered.
795 The ``filecomparison`` template is rendered.
796 """
796 """
797 ctx = webutil.changectx(web.repo, req)
797 ctx = webutil.changectx(web.repo, req)
798 if 'file' not in req.form:
798 if 'file' not in req.form:
799 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
799 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
800 path = webutil.cleanpath(web.repo, req.form['file'][0])
800 path = webutil.cleanpath(web.repo, req.form['file'][0])
801
801
802 parsecontext = lambda v: v == 'full' and -1 or int(v)
802 parsecontext = lambda v: v == 'full' and -1 or int(v)
803 if 'context' in req.form:
803 if 'context' in req.form:
804 context = parsecontext(req.form['context'][0])
804 context = parsecontext(req.form['context'][0])
805 else:
805 else:
806 context = parsecontext(web.config('web', 'comparisoncontext', '5'))
806 context = parsecontext(web.config('web', 'comparisoncontext', '5'))
807
807
808 def filelines(f):
808 def filelines(f):
809 if util.binary(f.data()):
809 if util.binary(f.data()):
810 mt = mimetypes.guess_type(f.path())[0]
810 mt = mimetypes.guess_type(f.path())[0]
811 if not mt:
811 if not mt:
812 mt = 'application/octet-stream'
812 mt = 'application/octet-stream'
813 return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))]
813 return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))]
814 return f.data().splitlines()
814 return f.data().splitlines()
815
815
816 fctx = None
816 fctx = None
817 parent = ctx.p1()
817 parent = ctx.p1()
818 leftrev = parent.rev()
818 leftrev = parent.rev()
819 leftnode = parent.node()
819 leftnode = parent.node()
820 rightrev = ctx.rev()
820 rightrev = ctx.rev()
821 rightnode = ctx.node()
821 rightnode = ctx.node()
822 if path in ctx:
822 if path in ctx:
823 fctx = ctx[path]
823 fctx = ctx[path]
824 rightlines = filelines(fctx)
824 rightlines = filelines(fctx)
825 if path not in parent:
825 if path not in parent:
826 leftlines = ()
826 leftlines = ()
827 else:
827 else:
828 pfctx = parent[path]
828 pfctx = parent[path]
829 leftlines = filelines(pfctx)
829 leftlines = filelines(pfctx)
830 else:
830 else:
831 rightlines = ()
831 rightlines = ()
832 pfctx = ctx.parents()[0][path]
832 pfctx = ctx.parents()[0][path]
833 leftlines = filelines(pfctx)
833 leftlines = filelines(pfctx)
834
834
835 comparison = webutil.compare(tmpl, context, leftlines, rightlines)
835 comparison = webutil.compare(tmpl, context, leftlines, rightlines)
836 if fctx is not None:
836 if fctx is not None:
837 rename = webutil.renamelink(fctx)
837 rename = webutil.renamelink(fctx)
838 ctx = fctx
838 ctx = fctx
839 else:
839 else:
840 rename = []
840 rename = []
841 ctx = ctx
841 ctx = ctx
842 return tmpl('filecomparison',
842 return tmpl('filecomparison',
843 file=path,
843 file=path,
844 symrev=webutil.symrevorshortnode(req, ctx),
844 symrev=webutil.symrevorshortnode(req, ctx),
845 rename=rename,
845 rename=rename,
846 leftrev=leftrev,
846 leftrev=leftrev,
847 leftnode=hex(leftnode),
847 leftnode=hex(leftnode),
848 rightrev=rightrev,
848 rightrev=rightrev,
849 rightnode=hex(rightnode),
849 rightnode=hex(rightnode),
850 comparison=comparison,
850 comparison=comparison,
851 **webutil.commonentry(web.repo, ctx))
851 **webutil.commonentry(web.repo, ctx))
852
852
853 @webcommand('annotate')
853 @webcommand('annotate')
854 def annotate(web, req, tmpl):
854 def annotate(web, req, tmpl):
855 """
855 """
856 /annotate/{revision}/{path}
856 /annotate/{revision}/{path}
857 ---------------------------
857 ---------------------------
858
858
859 Show changeset information for each line in a file.
859 Show changeset information for each line in a file.
860
860
861 The ``fileannotate`` template is rendered.
861 The ``fileannotate`` template is rendered.
862 """
862 """
863 fctx = webutil.filectx(web.repo, req)
863 fctx = webutil.filectx(web.repo, req)
864 f = fctx.path()
864 f = fctx.path()
865 parity = paritygen(web.stripecount)
865 parity = paritygen(web.stripecount)
866
866
867 # parents() is called once per line and several lines likely belong to
867 # parents() is called once per line and several lines likely belong to
868 # same revision. So it is worth caching.
868 # same revision. So it is worth caching.
869 # TODO there are still redundant operations within basefilectx.parents()
869 # TODO there are still redundant operations within basefilectx.parents()
870 # and from the fctx.annotate() call itself that could be cached.
870 # and from the fctx.annotate() call itself that could be cached.
871 parentscache = {}
871 parentscache = {}
872 def parents(f):
872 def parents(f):
873 rev = f.rev()
873 rev = f.rev()
874 if rev not in parentscache:
874 if rev not in parentscache:
875 parentscache[rev] = []
875 parentscache[rev] = []
876 for p in f.parents():
876 for p in f.parents():
877 entry = {
877 entry = {
878 'node': p.hex(),
878 'node': p.hex(),
879 'rev': p.rev(),
879 'rev': p.rev(),
880 }
880 }
881 parentscache[rev].append(entry)
881 parentscache[rev].append(entry)
882
882
883 for p in parentscache[rev]:
883 for p in parentscache[rev]:
884 yield p
884 yield p
885
885
886 def annotate(**map):
886 def annotate(**map):
887 if util.binary(fctx.data()):
887 if util.binary(fctx.data()):
888 mt = (mimetypes.guess_type(fctx.path())[0]
888 mt = (mimetypes.guess_type(fctx.path())[0]
889 or 'application/octet-stream')
889 or 'application/octet-stream')
890 lines = [((fctx.filectx(fctx.filerev()), 1), '(binary:%s)' % mt)]
890 lines = [((fctx.filectx(fctx.filerev()), 1), '(binary:%s)' % mt)]
891 else:
891 else:
892 lines = webutil.annotate(fctx, web.repo.ui)
892 lines = webutil.annotate(fctx, web.repo.ui)
893
893
894 previousrev = None
894 previousrev = None
895 blockparitygen = paritygen(1)
895 blockparitygen = paritygen(1)
896 for lineno, ((f, targetline), l) in enumerate(lines):
896 for lineno, ((f, targetline), l) in enumerate(lines):
897 rev = f.rev()
897 rev = f.rev()
898 if rev != previousrev:
898 if rev != previousrev:
899 blockhead = True
899 blockhead = True
900 blockparity = next(blockparitygen)
900 blockparity = next(blockparitygen)
901 else:
901 else:
902 blockhead = None
902 blockhead = None
903 previousrev = rev
903 previousrev = rev
904 yield {"parity": next(parity),
904 yield {"parity": next(parity),
905 "node": f.hex(),
905 "node": f.hex(),
906 "rev": rev,
906 "rev": rev,
907 "author": f.user(),
907 "author": f.user(),
908 "parents": parents(f),
908 "parents": parents(f),
909 "desc": f.description(),
909 "desc": f.description(),
910 "extra": f.extra(),
910 "extra": f.extra(),
911 "file": f.path(),
911 "file": f.path(),
912 "blockhead": blockhead,
912 "blockhead": blockhead,
913 "blockparity": blockparity,
913 "blockparity": blockparity,
914 "targetline": targetline,
914 "targetline": targetline,
915 "line": l,
915 "line": l,
916 "lineno": lineno + 1,
916 "lineno": lineno + 1,
917 "lineid": "l%d" % (lineno + 1),
917 "lineid": "l%d" % (lineno + 1),
918 "linenumber": "% 6d" % (lineno + 1),
918 "linenumber": "% 6d" % (lineno + 1),
919 "revdate": f.date()}
919 "revdate": f.date()}
920
920
921 return tmpl("fileannotate",
921 return tmpl("fileannotate",
922 file=f,
922 file=f,
923 annotate=annotate,
923 annotate=annotate,
924 path=webutil.up(f),
924 path=webutil.up(f),
925 symrev=webutil.symrevorshortnode(req, fctx),
925 symrev=webutil.symrevorshortnode(req, fctx),
926 rename=webutil.renamelink(fctx),
926 rename=webutil.renamelink(fctx),
927 permissions=fctx.manifest().flags(f),
927 permissions=fctx.manifest().flags(f),
928 **webutil.commonentry(web.repo, fctx))
928 **webutil.commonentry(web.repo, fctx))
929
929
930 @webcommand('filelog')
930 @webcommand('filelog')
931 def filelog(web, req, tmpl):
931 def filelog(web, req, tmpl):
932 """
932 """
933 /filelog/{revision}/{path}
933 /filelog/{revision}/{path}
934 --------------------------
934 --------------------------
935
935
936 Show information about the history of a file in the repository.
936 Show information about the history of a file in the repository.
937
937
938 The ``revcount`` query string argument can be defined to control the
938 The ``revcount`` query string argument can be defined to control the
939 maximum number of entries to show.
939 maximum number of entries to show.
940
940
941 The ``filelog`` template will be rendered.
941 The ``filelog`` template will be rendered.
942 """
942 """
943
943
944 try:
944 try:
945 fctx = webutil.filectx(web.repo, req)
945 fctx = webutil.filectx(web.repo, req)
946 f = fctx.path()
946 f = fctx.path()
947 fl = fctx.filelog()
947 fl = fctx.filelog()
948 except error.LookupError:
948 except error.LookupError:
949 f = webutil.cleanpath(web.repo, req.form['file'][0])
949 f = webutil.cleanpath(web.repo, req.form['file'][0])
950 fl = web.repo.file(f)
950 fl = web.repo.file(f)
951 numrevs = len(fl)
951 numrevs = len(fl)
952 if not numrevs: # file doesn't exist at all
952 if not numrevs: # file doesn't exist at all
953 raise
953 raise
954 rev = webutil.changectx(web.repo, req).rev()
954 rev = webutil.changectx(web.repo, req).rev()
955 first = fl.linkrev(0)
955 first = fl.linkrev(0)
956 if rev < first: # current rev is from before file existed
956 if rev < first: # current rev is from before file existed
957 raise
957 raise
958 frev = numrevs - 1
958 frev = numrevs - 1
959 while fl.linkrev(frev) > rev:
959 while fl.linkrev(frev) > rev:
960 frev -= 1
960 frev -= 1
961 fctx = web.repo.filectx(f, fl.linkrev(frev))
961 fctx = web.repo.filectx(f, fl.linkrev(frev))
962
962
963 revcount = web.maxshortchanges
963 revcount = web.maxshortchanges
964 if 'revcount' in req.form:
964 if 'revcount' in req.form:
965 try:
965 try:
966 revcount = int(req.form.get('revcount', [revcount])[0])
966 revcount = int(req.form.get('revcount', [revcount])[0])
967 revcount = max(revcount, 1)
967 revcount = max(revcount, 1)
968 tmpl.defaults['sessionvars']['revcount'] = revcount
968 tmpl.defaults['sessionvars']['revcount'] = revcount
969 except ValueError:
969 except ValueError:
970 pass
970 pass
971
971
972 lrange = webutil.linerange(req)
972 lrange = webutil.linerange(req)
973
973
974 lessvars = copy.copy(tmpl.defaults['sessionvars'])
974 lessvars = copy.copy(tmpl.defaults['sessionvars'])
975 lessvars['revcount'] = max(revcount / 2, 1)
975 lessvars['revcount'] = max(revcount / 2, 1)
976 morevars = copy.copy(tmpl.defaults['sessionvars'])
976 morevars = copy.copy(tmpl.defaults['sessionvars'])
977 morevars['revcount'] = revcount * 2
977 morevars['revcount'] = revcount * 2
978
978
979 patch = 'patch' in req.form
979 patch = 'patch' in req.form
980 if patch:
980 if patch:
981 lessvars['patch'] = morevars['patch'] = req.form['patch'][0]
981 lessvars['patch'] = morevars['patch'] = req.form['patch'][0]
982
982
983 count = fctx.filerev() + 1
983 count = fctx.filerev() + 1
984 start = max(0, count - revcount) # first rev on this page
984 start = max(0, count - revcount) # first rev on this page
985 end = min(count, start + revcount) # last rev on this page
985 end = min(count, start + revcount) # last rev on this page
986 parity = paritygen(web.stripecount, offset=start - end)
986 parity = paritygen(web.stripecount, offset=start - end)
987
987
988 repo = web.repo
988 repo = web.repo
989 revs = fctx.filelog().revs(start, end - 1)
989 revs = fctx.filelog().revs(start, end - 1)
990 entries = []
990 entries = []
991
991
992 diffstyle = web.config('web', 'style', 'paper')
992 diffstyle = web.config('web', 'style', 'paper')
993 if 'style' in req.form:
993 if 'style' in req.form:
994 diffstyle = req.form['style'][0]
994 diffstyle = req.form['style'][0]
995
995
996 def diff(fctx, linerange=None):
996 def diff(fctx, linerange=None):
997 ctx = fctx.changectx()
997 ctx = fctx.changectx()
998 basectx = ctx.p1()
998 basectx = ctx.p1()
999 path = fctx.path()
999 path = fctx.path()
1000 return webutil.diffs(web, tmpl, ctx, basectx, [path], diffstyle,
1000 return webutil.diffs(web, tmpl, ctx, basectx, [path], diffstyle,
1001 linerange=linerange)
1001 linerange=linerange,
1002 lineidprefix='%s-' % ctx.hex()[:12])
1002
1003
1003 linerange = None
1004 linerange = None
1004 if lrange is not None:
1005 if lrange is not None:
1005 linerange = webutil.formatlinerange(*lrange)
1006 linerange = webutil.formatlinerange(*lrange)
1006 # deactivate numeric nav links when linerange is specified as this
1007 # deactivate numeric nav links when linerange is specified as this
1007 # would required a dedicated "revnav" class
1008 # would required a dedicated "revnav" class
1008 nav = None
1009 nav = None
1009 ancestors = context.blockancestors(fctx, *lrange)
1010 ancestors = context.blockancestors(fctx, *lrange)
1010 for i, (c, lr) in enumerate(ancestors, 1):
1011 for i, (c, lr) in enumerate(ancestors, 1):
1011 diffs = None
1012 diffs = None
1012 if patch:
1013 if patch:
1013 diffs = diff(c, linerange=lr)
1014 diffs = diff(c, linerange=lr)
1014 # follow renames accross filtered (not in range) revisions
1015 # follow renames accross filtered (not in range) revisions
1015 path = c.path()
1016 path = c.path()
1016 entries.append(dict(
1017 entries.append(dict(
1017 parity=next(parity),
1018 parity=next(parity),
1018 filerev=c.rev(),
1019 filerev=c.rev(),
1019 file=path,
1020 file=path,
1020 diff=diffs,
1021 diff=diffs,
1021 linerange=webutil.formatlinerange(*lr),
1022 linerange=webutil.formatlinerange(*lr),
1022 **webutil.commonentry(repo, c)))
1023 **webutil.commonentry(repo, c)))
1023 if i == revcount:
1024 if i == revcount:
1024 break
1025 break
1025 lessvars['linerange'] = webutil.formatlinerange(*lrange)
1026 lessvars['linerange'] = webutil.formatlinerange(*lrange)
1026 morevars['linerange'] = lessvars['linerange']
1027 morevars['linerange'] = lessvars['linerange']
1027 else:
1028 else:
1028 for i in revs:
1029 for i in revs:
1029 iterfctx = fctx.filectx(i)
1030 iterfctx = fctx.filectx(i)
1030 diffs = None
1031 diffs = None
1031 if patch:
1032 if patch:
1032 diffs = diff(iterfctx)
1033 diffs = diff(iterfctx)
1033 entries.append(dict(
1034 entries.append(dict(
1034 parity=next(parity),
1035 parity=next(parity),
1035 filerev=i,
1036 filerev=i,
1036 file=f,
1037 file=f,
1037 diff=diffs,
1038 diff=diffs,
1038 rename=webutil.renamelink(iterfctx),
1039 rename=webutil.renamelink(iterfctx),
1039 **webutil.commonentry(repo, iterfctx)))
1040 **webutil.commonentry(repo, iterfctx)))
1040 entries.reverse()
1041 entries.reverse()
1041 revnav = webutil.filerevnav(web.repo, fctx.path())
1042 revnav = webutil.filerevnav(web.repo, fctx.path())
1042 nav = revnav.gen(end - 1, revcount, count)
1043 nav = revnav.gen(end - 1, revcount, count)
1043
1044
1044 latestentry = entries[:1]
1045 latestentry = entries[:1]
1045
1046
1046 return tmpl("filelog",
1047 return tmpl("filelog",
1047 file=f,
1048 file=f,
1048 nav=nav,
1049 nav=nav,
1049 symrev=webutil.symrevorshortnode(req, fctx),
1050 symrev=webutil.symrevorshortnode(req, fctx),
1050 entries=entries,
1051 entries=entries,
1051 patch=patch,
1052 patch=patch,
1052 latestentry=latestentry,
1053 latestentry=latestentry,
1053 linerange=linerange,
1054 linerange=linerange,
1054 revcount=revcount,
1055 revcount=revcount,
1055 morevars=morevars,
1056 morevars=morevars,
1056 lessvars=lessvars,
1057 lessvars=lessvars,
1057 **webutil.commonentry(web.repo, fctx))
1058 **webutil.commonentry(web.repo, fctx))
1058
1059
1059 @webcommand('archive')
1060 @webcommand('archive')
1060 def archive(web, req, tmpl):
1061 def archive(web, req, tmpl):
1061 """
1062 """
1062 /archive/{revision}.{format}[/{path}]
1063 /archive/{revision}.{format}[/{path}]
1063 -------------------------------------
1064 -------------------------------------
1064
1065
1065 Obtain an archive of repository content.
1066 Obtain an archive of repository content.
1066
1067
1067 The content and type of the archive is defined by a URL path parameter.
1068 The content and type of the archive is defined by a URL path parameter.
1068 ``format`` is the file extension of the archive type to be generated. e.g.
1069 ``format`` is the file extension of the archive type to be generated. e.g.
1069 ``zip`` or ``tar.bz2``. Not all archive types may be allowed by your
1070 ``zip`` or ``tar.bz2``. Not all archive types may be allowed by your
1070 server configuration.
1071 server configuration.
1071
1072
1072 The optional ``path`` URL parameter controls content to include in the
1073 The optional ``path`` URL parameter controls content to include in the
1073 archive. If omitted, every file in the specified revision is present in the
1074 archive. If omitted, every file in the specified revision is present in the
1074 archive. If included, only the specified file or contents of the specified
1075 archive. If included, only the specified file or contents of the specified
1075 directory will be included in the archive.
1076 directory will be included in the archive.
1076
1077
1077 No template is used for this handler. Raw, binary content is generated.
1078 No template is used for this handler. Raw, binary content is generated.
1078 """
1079 """
1079
1080
1080 type_ = req.form.get('type', [None])[0]
1081 type_ = req.form.get('type', [None])[0]
1081 allowed = web.configlist("web", "allow_archive")
1082 allowed = web.configlist("web", "allow_archive")
1082 key = req.form['node'][0]
1083 key = req.form['node'][0]
1083
1084
1084 if type_ not in web.archivespecs:
1085 if type_ not in web.archivespecs:
1085 msg = 'Unsupported archive type: %s' % type_
1086 msg = 'Unsupported archive type: %s' % type_
1086 raise ErrorResponse(HTTP_NOT_FOUND, msg)
1087 raise ErrorResponse(HTTP_NOT_FOUND, msg)
1087
1088
1088 if not ((type_ in allowed or
1089 if not ((type_ in allowed or
1089 web.configbool("web", "allow" + type_, False))):
1090 web.configbool("web", "allow" + type_, False))):
1090 msg = 'Archive type not allowed: %s' % type_
1091 msg = 'Archive type not allowed: %s' % type_
1091 raise ErrorResponse(HTTP_FORBIDDEN, msg)
1092 raise ErrorResponse(HTTP_FORBIDDEN, msg)
1092
1093
1093 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
1094 reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame))
1094 cnode = web.repo.lookup(key)
1095 cnode = web.repo.lookup(key)
1095 arch_version = key
1096 arch_version = key
1096 if cnode == key or key == 'tip':
1097 if cnode == key or key == 'tip':
1097 arch_version = short(cnode)
1098 arch_version = short(cnode)
1098 name = "%s-%s" % (reponame, arch_version)
1099 name = "%s-%s" % (reponame, arch_version)
1099
1100
1100 ctx = webutil.changectx(web.repo, req)
1101 ctx = webutil.changectx(web.repo, req)
1101 pats = []
1102 pats = []
1102 matchfn = scmutil.match(ctx, [])
1103 matchfn = scmutil.match(ctx, [])
1103 file = req.form.get('file', None)
1104 file = req.form.get('file', None)
1104 if file:
1105 if file:
1105 pats = ['path:' + file[0]]
1106 pats = ['path:' + file[0]]
1106 matchfn = scmutil.match(ctx, pats, default='path')
1107 matchfn = scmutil.match(ctx, pats, default='path')
1107 if pats:
1108 if pats:
1108 files = [f for f in ctx.manifest().keys() if matchfn(f)]
1109 files = [f for f in ctx.manifest().keys() if matchfn(f)]
1109 if not files:
1110 if not files:
1110 raise ErrorResponse(HTTP_NOT_FOUND,
1111 raise ErrorResponse(HTTP_NOT_FOUND,
1111 'file(s) not found: %s' % file[0])
1112 'file(s) not found: %s' % file[0])
1112
1113
1113 mimetype, artype, extension, encoding = web.archivespecs[type_]
1114 mimetype, artype, extension, encoding = web.archivespecs[type_]
1114 headers = [
1115 headers = [
1115 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
1116 ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension))
1116 ]
1117 ]
1117 if encoding:
1118 if encoding:
1118 headers.append(('Content-Encoding', encoding))
1119 headers.append(('Content-Encoding', encoding))
1119 req.headers.extend(headers)
1120 req.headers.extend(headers)
1120 req.respond(HTTP_OK, mimetype)
1121 req.respond(HTTP_OK, mimetype)
1121
1122
1122 archival.archive(web.repo, req, cnode, artype, prefix=name,
1123 archival.archive(web.repo, req, cnode, artype, prefix=name,
1123 matchfn=matchfn,
1124 matchfn=matchfn,
1124 subrepos=web.configbool("web", "archivesubrepos"))
1125 subrepos=web.configbool("web", "archivesubrepos"))
1125 return []
1126 return []
1126
1127
1127
1128
1128 @webcommand('static')
1129 @webcommand('static')
1129 def static(web, req, tmpl):
1130 def static(web, req, tmpl):
1130 fname = req.form['file'][0]
1131 fname = req.form['file'][0]
1131 # a repo owner may set web.static in .hg/hgrc to get any file
1132 # a repo owner may set web.static in .hg/hgrc to get any file
1132 # readable by the user running the CGI script
1133 # readable by the user running the CGI script
1133 static = web.config("web", "static", None, untrusted=False)
1134 static = web.config("web", "static", None, untrusted=False)
1134 if not static:
1135 if not static:
1135 tp = web.templatepath or templater.templatepaths()
1136 tp = web.templatepath or templater.templatepaths()
1136 if isinstance(tp, str):
1137 if isinstance(tp, str):
1137 tp = [tp]
1138 tp = [tp]
1138 static = [os.path.join(p, 'static') for p in tp]
1139 static = [os.path.join(p, 'static') for p in tp]
1139 staticfile(static, fname, req)
1140 staticfile(static, fname, req)
1140 return []
1141 return []
1141
1142
1142 @webcommand('graph')
1143 @webcommand('graph')
1143 def graph(web, req, tmpl):
1144 def graph(web, req, tmpl):
1144 """
1145 """
1145 /graph[/{revision}]
1146 /graph[/{revision}]
1146 -------------------
1147 -------------------
1147
1148
1148 Show information about the graphical topology of the repository.
1149 Show information about the graphical topology of the repository.
1149
1150
1150 Information rendered by this handler can be used to create visual
1151 Information rendered by this handler can be used to create visual
1151 representations of repository topology.
1152 representations of repository topology.
1152
1153
1153 The ``revision`` URL parameter controls the starting changeset.
1154 The ``revision`` URL parameter controls the starting changeset.
1154
1155
1155 The ``revcount`` query string argument can define the number of changesets
1156 The ``revcount`` query string argument can define the number of changesets
1156 to show information for.
1157 to show information for.
1157
1158
1158 This handler will render the ``graph`` template.
1159 This handler will render the ``graph`` template.
1159 """
1160 """
1160
1161
1161 if 'node' in req.form:
1162 if 'node' in req.form:
1162 ctx = webutil.changectx(web.repo, req)
1163 ctx = webutil.changectx(web.repo, req)
1163 symrev = webutil.symrevorshortnode(req, ctx)
1164 symrev = webutil.symrevorshortnode(req, ctx)
1164 else:
1165 else:
1165 ctx = web.repo['tip']
1166 ctx = web.repo['tip']
1166 symrev = 'tip'
1167 symrev = 'tip'
1167 rev = ctx.rev()
1168 rev = ctx.rev()
1168
1169
1169 bg_height = 39
1170 bg_height = 39
1170 revcount = web.maxshortchanges
1171 revcount = web.maxshortchanges
1171 if 'revcount' in req.form:
1172 if 'revcount' in req.form:
1172 try:
1173 try:
1173 revcount = int(req.form.get('revcount', [revcount])[0])
1174 revcount = int(req.form.get('revcount', [revcount])[0])
1174 revcount = max(revcount, 1)
1175 revcount = max(revcount, 1)
1175 tmpl.defaults['sessionvars']['revcount'] = revcount
1176 tmpl.defaults['sessionvars']['revcount'] = revcount
1176 except ValueError:
1177 except ValueError:
1177 pass
1178 pass
1178
1179
1179 lessvars = copy.copy(tmpl.defaults['sessionvars'])
1180 lessvars = copy.copy(tmpl.defaults['sessionvars'])
1180 lessvars['revcount'] = max(revcount / 2, 1)
1181 lessvars['revcount'] = max(revcount / 2, 1)
1181 morevars = copy.copy(tmpl.defaults['sessionvars'])
1182 morevars = copy.copy(tmpl.defaults['sessionvars'])
1182 morevars['revcount'] = revcount * 2
1183 morevars['revcount'] = revcount * 2
1183
1184
1184 count = len(web.repo)
1185 count = len(web.repo)
1185 pos = rev
1186 pos = rev
1186
1187
1187 uprev = min(max(0, count - 1), rev + revcount)
1188 uprev = min(max(0, count - 1), rev + revcount)
1188 downrev = max(0, rev - revcount)
1189 downrev = max(0, rev - revcount)
1189 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
1190 changenav = webutil.revnav(web.repo).gen(pos, revcount, count)
1190
1191
1191 tree = []
1192 tree = []
1192 if pos != -1:
1193 if pos != -1:
1193 allrevs = web.repo.changelog.revs(pos, 0)
1194 allrevs = web.repo.changelog.revs(pos, 0)
1194 revs = []
1195 revs = []
1195 for i in allrevs:
1196 for i in allrevs:
1196 revs.append(i)
1197 revs.append(i)
1197 if len(revs) >= revcount:
1198 if len(revs) >= revcount:
1198 break
1199 break
1199
1200
1200 # We have to feed a baseset to dagwalker as it is expecting smartset
1201 # We have to feed a baseset to dagwalker as it is expecting smartset
1201 # object. This does not have a big impact on hgweb performance itself
1202 # object. This does not have a big impact on hgweb performance itself
1202 # since hgweb graphing code is not itself lazy yet.
1203 # since hgweb graphing code is not itself lazy yet.
1203 dag = graphmod.dagwalker(web.repo, smartset.baseset(revs))
1204 dag = graphmod.dagwalker(web.repo, smartset.baseset(revs))
1204 # As we said one line above... not lazy.
1205 # As we said one line above... not lazy.
1205 tree = list(graphmod.colored(dag, web.repo))
1206 tree = list(graphmod.colored(dag, web.repo))
1206
1207
1207 def getcolumns(tree):
1208 def getcolumns(tree):
1208 cols = 0
1209 cols = 0
1209 for (id, type, ctx, vtx, edges) in tree:
1210 for (id, type, ctx, vtx, edges) in tree:
1210 if type != graphmod.CHANGESET:
1211 if type != graphmod.CHANGESET:
1211 continue
1212 continue
1212 cols = max(cols, max([edge[0] for edge in edges] or [0]),
1213 cols = max(cols, max([edge[0] for edge in edges] or [0]),
1213 max([edge[1] for edge in edges] or [0]))
1214 max([edge[1] for edge in edges] or [0]))
1214 return cols
1215 return cols
1215
1216
1216 def graphdata(usetuples, encodestr):
1217 def graphdata(usetuples, encodestr):
1217 data = []
1218 data = []
1218
1219
1219 row = 0
1220 row = 0
1220 for (id, type, ctx, vtx, edges) in tree:
1221 for (id, type, ctx, vtx, edges) in tree:
1221 if type != graphmod.CHANGESET:
1222 if type != graphmod.CHANGESET:
1222 continue
1223 continue
1223 node = str(ctx)
1224 node = str(ctx)
1224 age = encodestr(templatefilters.age(ctx.date()))
1225 age = encodestr(templatefilters.age(ctx.date()))
1225 desc = templatefilters.firstline(encodestr(ctx.description()))
1226 desc = templatefilters.firstline(encodestr(ctx.description()))
1226 desc = cgi.escape(templatefilters.nonempty(desc))
1227 desc = cgi.escape(templatefilters.nonempty(desc))
1227 user = cgi.escape(templatefilters.person(encodestr(ctx.user())))
1228 user = cgi.escape(templatefilters.person(encodestr(ctx.user())))
1228 branch = cgi.escape(encodestr(ctx.branch()))
1229 branch = cgi.escape(encodestr(ctx.branch()))
1229 try:
1230 try:
1230 branchnode = web.repo.branchtip(branch)
1231 branchnode = web.repo.branchtip(branch)
1231 except error.RepoLookupError:
1232 except error.RepoLookupError:
1232 branchnode = None
1233 branchnode = None
1233 branch = branch, branchnode == ctx.node()
1234 branch = branch, branchnode == ctx.node()
1234
1235
1235 if usetuples:
1236 if usetuples:
1236 data.append((node, vtx, edges, desc, user, age, branch,
1237 data.append((node, vtx, edges, desc, user, age, branch,
1237 [cgi.escape(encodestr(x)) for x in ctx.tags()],
1238 [cgi.escape(encodestr(x)) for x in ctx.tags()],
1238 [cgi.escape(encodestr(x))
1239 [cgi.escape(encodestr(x))
1239 for x in ctx.bookmarks()]))
1240 for x in ctx.bookmarks()]))
1240 else:
1241 else:
1241 edgedata = [{'col': edge[0], 'nextcol': edge[1],
1242 edgedata = [{'col': edge[0], 'nextcol': edge[1],
1242 'color': (edge[2] - 1) % 6 + 1,
1243 'color': (edge[2] - 1) % 6 + 1,
1243 'width': edge[3], 'bcolor': edge[4]}
1244 'width': edge[3], 'bcolor': edge[4]}
1244 for edge in edges]
1245 for edge in edges]
1245
1246
1246 data.append(
1247 data.append(
1247 {'node': node,
1248 {'node': node,
1248 'col': vtx[0],
1249 'col': vtx[0],
1249 'color': (vtx[1] - 1) % 6 + 1,
1250 'color': (vtx[1] - 1) % 6 + 1,
1250 'edges': edgedata,
1251 'edges': edgedata,
1251 'row': row,
1252 'row': row,
1252 'nextrow': row + 1,
1253 'nextrow': row + 1,
1253 'desc': desc,
1254 'desc': desc,
1254 'user': user,
1255 'user': user,
1255 'age': age,
1256 'age': age,
1256 'bookmarks': webutil.nodebookmarksdict(
1257 'bookmarks': webutil.nodebookmarksdict(
1257 web.repo, ctx.node()),
1258 web.repo, ctx.node()),
1258 'branches': webutil.nodebranchdict(web.repo, ctx),
1259 'branches': webutil.nodebranchdict(web.repo, ctx),
1259 'inbranch': webutil.nodeinbranch(web.repo, ctx),
1260 'inbranch': webutil.nodeinbranch(web.repo, ctx),
1260 'tags': webutil.nodetagsdict(web.repo, ctx.node())})
1261 'tags': webutil.nodetagsdict(web.repo, ctx.node())})
1261
1262
1262 row += 1
1263 row += 1
1263
1264
1264 return data
1265 return data
1265
1266
1266 cols = getcolumns(tree)
1267 cols = getcolumns(tree)
1267 rows = len(tree)
1268 rows = len(tree)
1268 canvasheight = (rows + 1) * bg_height - 27
1269 canvasheight = (rows + 1) * bg_height - 27
1269
1270
1270 return tmpl('graph', rev=rev, symrev=symrev, revcount=revcount,
1271 return tmpl('graph', rev=rev, symrev=symrev, revcount=revcount,
1271 uprev=uprev,
1272 uprev=uprev,
1272 lessvars=lessvars, morevars=morevars, downrev=downrev,
1273 lessvars=lessvars, morevars=morevars, downrev=downrev,
1273 cols=cols, rows=rows,
1274 cols=cols, rows=rows,
1274 canvaswidth=(cols + 1) * bg_height,
1275 canvaswidth=(cols + 1) * bg_height,
1275 truecanvasheight=rows * bg_height,
1276 truecanvasheight=rows * bg_height,
1276 canvasheight=canvasheight, bg_height=bg_height,
1277 canvasheight=canvasheight, bg_height=bg_height,
1277 # {jsdata} will be passed to |json, so it must be in utf-8
1278 # {jsdata} will be passed to |json, so it must be in utf-8
1278 jsdata=lambda **x: graphdata(True, encoding.fromlocal),
1279 jsdata=lambda **x: graphdata(True, encoding.fromlocal),
1279 nodes=lambda **x: graphdata(False, str),
1280 nodes=lambda **x: graphdata(False, str),
1280 node=ctx.hex(), changenav=changenav)
1281 node=ctx.hex(), changenav=changenav)
1281
1282
1282 def _getdoc(e):
1283 def _getdoc(e):
1283 doc = e[0].__doc__
1284 doc = e[0].__doc__
1284 if doc:
1285 if doc:
1285 doc = _(doc).partition('\n')[0]
1286 doc = _(doc).partition('\n')[0]
1286 else:
1287 else:
1287 doc = _('(no help text available)')
1288 doc = _('(no help text available)')
1288 return doc
1289 return doc
1289
1290
1290 @webcommand('help')
1291 @webcommand('help')
1291 def help(web, req, tmpl):
1292 def help(web, req, tmpl):
1292 """
1293 """
1293 /help[/{topic}]
1294 /help[/{topic}]
1294 ---------------
1295 ---------------
1295
1296
1296 Render help documentation.
1297 Render help documentation.
1297
1298
1298 This web command is roughly equivalent to :hg:`help`. If a ``topic``
1299 This web command is roughly equivalent to :hg:`help`. If a ``topic``
1299 is defined, that help topic will be rendered. If not, an index of
1300 is defined, that help topic will be rendered. If not, an index of
1300 available help topics will be rendered.
1301 available help topics will be rendered.
1301
1302
1302 The ``help`` template will be rendered when requesting help for a topic.
1303 The ``help`` template will be rendered when requesting help for a topic.
1303 ``helptopics`` will be rendered for the index of help topics.
1304 ``helptopics`` will be rendered for the index of help topics.
1304 """
1305 """
1305 from .. import commands, help as helpmod # avoid cycle
1306 from .. import commands, help as helpmod # avoid cycle
1306
1307
1307 topicname = req.form.get('node', [None])[0]
1308 topicname = req.form.get('node', [None])[0]
1308 if not topicname:
1309 if not topicname:
1309 def topics(**map):
1310 def topics(**map):
1310 for entries, summary, _doc in helpmod.helptable:
1311 for entries, summary, _doc in helpmod.helptable:
1311 yield {'topic': entries[0], 'summary': summary}
1312 yield {'topic': entries[0], 'summary': summary}
1312
1313
1313 early, other = [], []
1314 early, other = [], []
1314 primary = lambda s: s.partition('|')[0]
1315 primary = lambda s: s.partition('|')[0]
1315 for c, e in commands.table.iteritems():
1316 for c, e in commands.table.iteritems():
1316 doc = _getdoc(e)
1317 doc = _getdoc(e)
1317 if 'DEPRECATED' in doc or c.startswith('debug'):
1318 if 'DEPRECATED' in doc or c.startswith('debug'):
1318 continue
1319 continue
1319 cmd = primary(c)
1320 cmd = primary(c)
1320 if cmd.startswith('^'):
1321 if cmd.startswith('^'):
1321 early.append((cmd[1:], doc))
1322 early.append((cmd[1:], doc))
1322 else:
1323 else:
1323 other.append((cmd, doc))
1324 other.append((cmd, doc))
1324
1325
1325 early.sort()
1326 early.sort()
1326 other.sort()
1327 other.sort()
1327
1328
1328 def earlycommands(**map):
1329 def earlycommands(**map):
1329 for c, doc in early:
1330 for c, doc in early:
1330 yield {'topic': c, 'summary': doc}
1331 yield {'topic': c, 'summary': doc}
1331
1332
1332 def othercommands(**map):
1333 def othercommands(**map):
1333 for c, doc in other:
1334 for c, doc in other:
1334 yield {'topic': c, 'summary': doc}
1335 yield {'topic': c, 'summary': doc}
1335
1336
1336 return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
1337 return tmpl('helptopics', topics=topics, earlycommands=earlycommands,
1337 othercommands=othercommands, title='Index')
1338 othercommands=othercommands, title='Index')
1338
1339
1339 # Render an index of sub-topics.
1340 # Render an index of sub-topics.
1340 if topicname in helpmod.subtopics:
1341 if topicname in helpmod.subtopics:
1341 topics = []
1342 topics = []
1342 for entries, summary, _doc in helpmod.subtopics[topicname]:
1343 for entries, summary, _doc in helpmod.subtopics[topicname]:
1343 topics.append({
1344 topics.append({
1344 'topic': '%s.%s' % (topicname, entries[0]),
1345 'topic': '%s.%s' % (topicname, entries[0]),
1345 'basename': entries[0],
1346 'basename': entries[0],
1346 'summary': summary,
1347 'summary': summary,
1347 })
1348 })
1348
1349
1349 return tmpl('helptopics', topics=topics, title=topicname,
1350 return tmpl('helptopics', topics=topics, title=topicname,
1350 subindex=True)
1351 subindex=True)
1351
1352
1352 u = webutil.wsgiui.load()
1353 u = webutil.wsgiui.load()
1353 u.verbose = True
1354 u.verbose = True
1354
1355
1355 # Render a page from a sub-topic.
1356 # Render a page from a sub-topic.
1356 if '.' in topicname:
1357 if '.' in topicname:
1357 # TODO implement support for rendering sections, like
1358 # TODO implement support for rendering sections, like
1358 # `hg help` works.
1359 # `hg help` works.
1359 topic, subtopic = topicname.split('.', 1)
1360 topic, subtopic = topicname.split('.', 1)
1360 if topic not in helpmod.subtopics:
1361 if topic not in helpmod.subtopics:
1361 raise ErrorResponse(HTTP_NOT_FOUND)
1362 raise ErrorResponse(HTTP_NOT_FOUND)
1362 else:
1363 else:
1363 topic = topicname
1364 topic = topicname
1364 subtopic = None
1365 subtopic = None
1365
1366
1366 try:
1367 try:
1367 doc = helpmod.help_(u, topic, subtopic=subtopic)
1368 doc = helpmod.help_(u, topic, subtopic=subtopic)
1368 except error.UnknownCommand:
1369 except error.UnknownCommand:
1369 raise ErrorResponse(HTTP_NOT_FOUND)
1370 raise ErrorResponse(HTTP_NOT_FOUND)
1370 return tmpl('help', topic=topicname, doc=doc)
1371 return tmpl('help', topic=topicname, doc=doc)
1371
1372
1372 # tell hggettext to extract docstrings from these functions:
1373 # tell hggettext to extract docstrings from these functions:
1373 i18nfunctions = commands.values()
1374 i18nfunctions = commands.values()
@@ -1,627 +1,628 b''
1 # hgweb/webutil.py - utility library for the web interface.
1 # hgweb/webutil.py - utility library for the web interface.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import copy
11 import copy
12 import difflib
12 import difflib
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, nullid, short
17 from ..node import hex, nullid, short
18
18
19 from .common import (
19 from .common import (
20 ErrorResponse,
20 ErrorResponse,
21 HTTP_BAD_REQUEST,
21 HTTP_BAD_REQUEST,
22 HTTP_NOT_FOUND,
22 HTTP_NOT_FOUND,
23 paritygen,
23 paritygen,
24 )
24 )
25
25
26 from .. import (
26 from .. import (
27 context,
27 context,
28 error,
28 error,
29 match,
29 match,
30 patch,
30 patch,
31 pathutil,
31 pathutil,
32 templatefilters,
32 templatefilters,
33 ui as uimod,
33 ui as uimod,
34 util,
34 util,
35 )
35 )
36
36
37 def up(p):
37 def up(p):
38 if p[0] != "/":
38 if p[0] != "/":
39 p = "/" + p
39 p = "/" + p
40 if p[-1] == "/":
40 if p[-1] == "/":
41 p = p[:-1]
41 p = p[:-1]
42 up = os.path.dirname(p)
42 up = os.path.dirname(p)
43 if up == "/":
43 if up == "/":
44 return "/"
44 return "/"
45 return up + "/"
45 return up + "/"
46
46
47 def _navseq(step, firststep=None):
47 def _navseq(step, firststep=None):
48 if firststep:
48 if firststep:
49 yield firststep
49 yield firststep
50 if firststep >= 20 and firststep <= 40:
50 if firststep >= 20 and firststep <= 40:
51 firststep = 50
51 firststep = 50
52 yield firststep
52 yield firststep
53 assert step > 0
53 assert step > 0
54 assert firststep > 0
54 assert firststep > 0
55 while step <= firststep:
55 while step <= firststep:
56 step *= 10
56 step *= 10
57 while True:
57 while True:
58 yield 1 * step
58 yield 1 * step
59 yield 3 * step
59 yield 3 * step
60 step *= 10
60 step *= 10
61
61
62 class revnav(object):
62 class revnav(object):
63
63
64 def __init__(self, repo):
64 def __init__(self, repo):
65 """Navigation generation object
65 """Navigation generation object
66
66
67 :repo: repo object we generate nav for
67 :repo: repo object we generate nav for
68 """
68 """
69 # used for hex generation
69 # used for hex generation
70 self._revlog = repo.changelog
70 self._revlog = repo.changelog
71
71
72 def __nonzero__(self):
72 def __nonzero__(self):
73 """return True if any revision to navigate over"""
73 """return True if any revision to navigate over"""
74 return self._first() is not None
74 return self._first() is not None
75
75
76 __bool__ = __nonzero__
76 __bool__ = __nonzero__
77
77
78 def _first(self):
78 def _first(self):
79 """return the minimum non-filtered changeset or None"""
79 """return the minimum non-filtered changeset or None"""
80 try:
80 try:
81 return next(iter(self._revlog))
81 return next(iter(self._revlog))
82 except StopIteration:
82 except StopIteration:
83 return None
83 return None
84
84
85 def hex(self, rev):
85 def hex(self, rev):
86 return hex(self._revlog.node(rev))
86 return hex(self._revlog.node(rev))
87
87
88 def gen(self, pos, pagelen, limit):
88 def gen(self, pos, pagelen, limit):
89 """computes label and revision id for navigation link
89 """computes label and revision id for navigation link
90
90
91 :pos: is the revision relative to which we generate navigation.
91 :pos: is the revision relative to which we generate navigation.
92 :pagelen: the size of each navigation page
92 :pagelen: the size of each navigation page
93 :limit: how far shall we link
93 :limit: how far shall we link
94
94
95 The return is:
95 The return is:
96 - a single element tuple
96 - a single element tuple
97 - containing a dictionary with a `before` and `after` key
97 - containing a dictionary with a `before` and `after` key
98 - values are generator functions taking arbitrary number of kwargs
98 - values are generator functions taking arbitrary number of kwargs
99 - yield items are dictionaries with `label` and `node` keys
99 - yield items are dictionaries with `label` and `node` keys
100 """
100 """
101 if not self:
101 if not self:
102 # empty repo
102 # empty repo
103 return ({'before': (), 'after': ()},)
103 return ({'before': (), 'after': ()},)
104
104
105 targets = []
105 targets = []
106 for f in _navseq(1, pagelen):
106 for f in _navseq(1, pagelen):
107 if f > limit:
107 if f > limit:
108 break
108 break
109 targets.append(pos + f)
109 targets.append(pos + f)
110 targets.append(pos - f)
110 targets.append(pos - f)
111 targets.sort()
111 targets.sort()
112
112
113 first = self._first()
113 first = self._first()
114 navbefore = [("(%i)" % first, self.hex(first))]
114 navbefore = [("(%i)" % first, self.hex(first))]
115 navafter = []
115 navafter = []
116 for rev in targets:
116 for rev in targets:
117 if rev not in self._revlog:
117 if rev not in self._revlog:
118 continue
118 continue
119 if pos < rev < limit:
119 if pos < rev < limit:
120 navafter.append(("+%d" % abs(rev - pos), self.hex(rev)))
120 navafter.append(("+%d" % abs(rev - pos), self.hex(rev)))
121 if 0 < rev < pos:
121 if 0 < rev < pos:
122 navbefore.append(("-%d" % abs(rev - pos), self.hex(rev)))
122 navbefore.append(("-%d" % abs(rev - pos), self.hex(rev)))
123
123
124
124
125 navafter.append(("tip", "tip"))
125 navafter.append(("tip", "tip"))
126
126
127 data = lambda i: {"label": i[0], "node": i[1]}
127 data = lambda i: {"label": i[0], "node": i[1]}
128 return ({'before': lambda **map: (data(i) for i in navbefore),
128 return ({'before': lambda **map: (data(i) for i in navbefore),
129 'after': lambda **map: (data(i) for i in navafter)},)
129 'after': lambda **map: (data(i) for i in navafter)},)
130
130
131 class filerevnav(revnav):
131 class filerevnav(revnav):
132
132
133 def __init__(self, repo, path):
133 def __init__(self, repo, path):
134 """Navigation generation object
134 """Navigation generation object
135
135
136 :repo: repo object we generate nav for
136 :repo: repo object we generate nav for
137 :path: path of the file we generate nav for
137 :path: path of the file we generate nav for
138 """
138 """
139 # used for iteration
139 # used for iteration
140 self._changelog = repo.unfiltered().changelog
140 self._changelog = repo.unfiltered().changelog
141 # used for hex generation
141 # used for hex generation
142 self._revlog = repo.file(path)
142 self._revlog = repo.file(path)
143
143
144 def hex(self, rev):
144 def hex(self, rev):
145 return hex(self._changelog.node(self._revlog.linkrev(rev)))
145 return hex(self._changelog.node(self._revlog.linkrev(rev)))
146
146
147 class _siblings(object):
147 class _siblings(object):
148 def __init__(self, siblings=None, hiderev=None):
148 def __init__(self, siblings=None, hiderev=None):
149 if siblings is None:
149 if siblings is None:
150 siblings = []
150 siblings = []
151 self.siblings = [s for s in siblings if s.node() != nullid]
151 self.siblings = [s for s in siblings if s.node() != nullid]
152 if len(self.siblings) == 1 and self.siblings[0].rev() == hiderev:
152 if len(self.siblings) == 1 and self.siblings[0].rev() == hiderev:
153 self.siblings = []
153 self.siblings = []
154
154
155 def __iter__(self):
155 def __iter__(self):
156 for s in self.siblings:
156 for s in self.siblings:
157 d = {
157 d = {
158 'node': s.hex(),
158 'node': s.hex(),
159 'rev': s.rev(),
159 'rev': s.rev(),
160 'user': s.user(),
160 'user': s.user(),
161 'date': s.date(),
161 'date': s.date(),
162 'description': s.description(),
162 'description': s.description(),
163 'branch': s.branch(),
163 'branch': s.branch(),
164 }
164 }
165 if util.safehasattr(s, 'path'):
165 if util.safehasattr(s, 'path'):
166 d['file'] = s.path()
166 d['file'] = s.path()
167 yield d
167 yield d
168
168
169 def __len__(self):
169 def __len__(self):
170 return len(self.siblings)
170 return len(self.siblings)
171
171
172 def annotate(fctx, ui):
172 def annotate(fctx, ui):
173 diffopts = patch.difffeatureopts(ui, untrusted=True,
173 diffopts = patch.difffeatureopts(ui, untrusted=True,
174 section='annotate', whitespace=True)
174 section='annotate', whitespace=True)
175 return fctx.annotate(follow=True, linenumber=True, diffopts=diffopts)
175 return fctx.annotate(follow=True, linenumber=True, diffopts=diffopts)
176
176
177 def parents(ctx, hide=None):
177 def parents(ctx, hide=None):
178 if isinstance(ctx, context.basefilectx):
178 if isinstance(ctx, context.basefilectx):
179 introrev = ctx.introrev()
179 introrev = ctx.introrev()
180 if ctx.changectx().rev() != introrev:
180 if ctx.changectx().rev() != introrev:
181 return _siblings([ctx.repo()[introrev]], hide)
181 return _siblings([ctx.repo()[introrev]], hide)
182 return _siblings(ctx.parents(), hide)
182 return _siblings(ctx.parents(), hide)
183
183
184 def children(ctx, hide=None):
184 def children(ctx, hide=None):
185 return _siblings(ctx.children(), hide)
185 return _siblings(ctx.children(), hide)
186
186
187 def renamelink(fctx):
187 def renamelink(fctx):
188 r = fctx.renamed()
188 r = fctx.renamed()
189 if r:
189 if r:
190 return [{'file': r[0], 'node': hex(r[1])}]
190 return [{'file': r[0], 'node': hex(r[1])}]
191 return []
191 return []
192
192
193 def nodetagsdict(repo, node):
193 def nodetagsdict(repo, node):
194 return [{"name": i} for i in repo.nodetags(node)]
194 return [{"name": i} for i in repo.nodetags(node)]
195
195
196 def nodebookmarksdict(repo, node):
196 def nodebookmarksdict(repo, node):
197 return [{"name": i} for i in repo.nodebookmarks(node)]
197 return [{"name": i} for i in repo.nodebookmarks(node)]
198
198
199 def nodebranchdict(repo, ctx):
199 def nodebranchdict(repo, ctx):
200 branches = []
200 branches = []
201 branch = ctx.branch()
201 branch = ctx.branch()
202 # If this is an empty repo, ctx.node() == nullid,
202 # If this is an empty repo, ctx.node() == nullid,
203 # ctx.branch() == 'default'.
203 # ctx.branch() == 'default'.
204 try:
204 try:
205 branchnode = repo.branchtip(branch)
205 branchnode = repo.branchtip(branch)
206 except error.RepoLookupError:
206 except error.RepoLookupError:
207 branchnode = None
207 branchnode = None
208 if branchnode == ctx.node():
208 if branchnode == ctx.node():
209 branches.append({"name": branch})
209 branches.append({"name": branch})
210 return branches
210 return branches
211
211
212 def nodeinbranch(repo, ctx):
212 def nodeinbranch(repo, ctx):
213 branches = []
213 branches = []
214 branch = ctx.branch()
214 branch = ctx.branch()
215 try:
215 try:
216 branchnode = repo.branchtip(branch)
216 branchnode = repo.branchtip(branch)
217 except error.RepoLookupError:
217 except error.RepoLookupError:
218 branchnode = None
218 branchnode = None
219 if branch != 'default' and branchnode != ctx.node():
219 if branch != 'default' and branchnode != ctx.node():
220 branches.append({"name": branch})
220 branches.append({"name": branch})
221 return branches
221 return branches
222
222
223 def nodebranchnodefault(ctx):
223 def nodebranchnodefault(ctx):
224 branches = []
224 branches = []
225 branch = ctx.branch()
225 branch = ctx.branch()
226 if branch != 'default':
226 if branch != 'default':
227 branches.append({"name": branch})
227 branches.append({"name": branch})
228 return branches
228 return branches
229
229
230 def showtag(repo, tmpl, t1, node=nullid, **args):
230 def showtag(repo, tmpl, t1, node=nullid, **args):
231 for t in repo.nodetags(node):
231 for t in repo.nodetags(node):
232 yield tmpl(t1, tag=t, **args)
232 yield tmpl(t1, tag=t, **args)
233
233
234 def showbookmark(repo, tmpl, t1, node=nullid, **args):
234 def showbookmark(repo, tmpl, t1, node=nullid, **args):
235 for t in repo.nodebookmarks(node):
235 for t in repo.nodebookmarks(node):
236 yield tmpl(t1, bookmark=t, **args)
236 yield tmpl(t1, bookmark=t, **args)
237
237
238 def branchentries(repo, stripecount, limit=0):
238 def branchentries(repo, stripecount, limit=0):
239 tips = []
239 tips = []
240 heads = repo.heads()
240 heads = repo.heads()
241 parity = paritygen(stripecount)
241 parity = paritygen(stripecount)
242 sortkey = lambda item: (not item[1], item[0].rev())
242 sortkey = lambda item: (not item[1], item[0].rev())
243
243
244 def entries(**map):
244 def entries(**map):
245 count = 0
245 count = 0
246 if not tips:
246 if not tips:
247 for tag, hs, tip, closed in repo.branchmap().iterbranches():
247 for tag, hs, tip, closed in repo.branchmap().iterbranches():
248 tips.append((repo[tip], closed))
248 tips.append((repo[tip], closed))
249 for ctx, closed in sorted(tips, key=sortkey, reverse=True):
249 for ctx, closed in sorted(tips, key=sortkey, reverse=True):
250 if limit > 0 and count >= limit:
250 if limit > 0 and count >= limit:
251 return
251 return
252 count += 1
252 count += 1
253 if closed:
253 if closed:
254 status = 'closed'
254 status = 'closed'
255 elif ctx.node() not in heads:
255 elif ctx.node() not in heads:
256 status = 'inactive'
256 status = 'inactive'
257 else:
257 else:
258 status = 'open'
258 status = 'open'
259 yield {
259 yield {
260 'parity': next(parity),
260 'parity': next(parity),
261 'branch': ctx.branch(),
261 'branch': ctx.branch(),
262 'status': status,
262 'status': status,
263 'node': ctx.hex(),
263 'node': ctx.hex(),
264 'date': ctx.date()
264 'date': ctx.date()
265 }
265 }
266
266
267 return entries
267 return entries
268
268
269 def cleanpath(repo, path):
269 def cleanpath(repo, path):
270 path = path.lstrip('/')
270 path = path.lstrip('/')
271 return pathutil.canonpath(repo.root, '', path)
271 return pathutil.canonpath(repo.root, '', path)
272
272
273 def changeidctx(repo, changeid):
273 def changeidctx(repo, changeid):
274 try:
274 try:
275 ctx = repo[changeid]
275 ctx = repo[changeid]
276 except error.RepoError:
276 except error.RepoError:
277 man = repo.manifestlog._revlog
277 man = repo.manifestlog._revlog
278 ctx = repo[man.linkrev(man.rev(man.lookup(changeid)))]
278 ctx = repo[man.linkrev(man.rev(man.lookup(changeid)))]
279
279
280 return ctx
280 return ctx
281
281
282 def changectx(repo, req):
282 def changectx(repo, req):
283 changeid = "tip"
283 changeid = "tip"
284 if 'node' in req.form:
284 if 'node' in req.form:
285 changeid = req.form['node'][0]
285 changeid = req.form['node'][0]
286 ipos = changeid.find(':')
286 ipos = changeid.find(':')
287 if ipos != -1:
287 if ipos != -1:
288 changeid = changeid[(ipos + 1):]
288 changeid = changeid[(ipos + 1):]
289 elif 'manifest' in req.form:
289 elif 'manifest' in req.form:
290 changeid = req.form['manifest'][0]
290 changeid = req.form['manifest'][0]
291
291
292 return changeidctx(repo, changeid)
292 return changeidctx(repo, changeid)
293
293
294 def basechangectx(repo, req):
294 def basechangectx(repo, req):
295 if 'node' in req.form:
295 if 'node' in req.form:
296 changeid = req.form['node'][0]
296 changeid = req.form['node'][0]
297 ipos = changeid.find(':')
297 ipos = changeid.find(':')
298 if ipos != -1:
298 if ipos != -1:
299 changeid = changeid[:ipos]
299 changeid = changeid[:ipos]
300 return changeidctx(repo, changeid)
300 return changeidctx(repo, changeid)
301
301
302 return None
302 return None
303
303
304 def filectx(repo, req):
304 def filectx(repo, req):
305 if 'file' not in req.form:
305 if 'file' not in req.form:
306 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
306 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
307 path = cleanpath(repo, req.form['file'][0])
307 path = cleanpath(repo, req.form['file'][0])
308 if 'node' in req.form:
308 if 'node' in req.form:
309 changeid = req.form['node'][0]
309 changeid = req.form['node'][0]
310 elif 'filenode' in req.form:
310 elif 'filenode' in req.form:
311 changeid = req.form['filenode'][0]
311 changeid = req.form['filenode'][0]
312 else:
312 else:
313 raise ErrorResponse(HTTP_NOT_FOUND, 'node or filenode not given')
313 raise ErrorResponse(HTTP_NOT_FOUND, 'node or filenode not given')
314 try:
314 try:
315 fctx = repo[changeid][path]
315 fctx = repo[changeid][path]
316 except error.RepoError:
316 except error.RepoError:
317 fctx = repo.filectx(path, fileid=changeid)
317 fctx = repo.filectx(path, fileid=changeid)
318
318
319 return fctx
319 return fctx
320
320
321 def linerange(req):
321 def linerange(req):
322 linerange = req.form.get('linerange')
322 linerange = req.form.get('linerange')
323 if linerange is None:
323 if linerange is None:
324 return None
324 return None
325 if len(linerange) > 1:
325 if len(linerange) > 1:
326 raise ErrorResponse(HTTP_BAD_REQUEST,
326 raise ErrorResponse(HTTP_BAD_REQUEST,
327 'redundant linerange parameter')
327 'redundant linerange parameter')
328 try:
328 try:
329 fromline, toline = map(int, linerange[0].split(':', 1))
329 fromline, toline = map(int, linerange[0].split(':', 1))
330 except ValueError:
330 except ValueError:
331 raise ErrorResponse(HTTP_BAD_REQUEST,
331 raise ErrorResponse(HTTP_BAD_REQUEST,
332 'invalid linerange parameter')
332 'invalid linerange parameter')
333 try:
333 try:
334 return util.processlinerange(fromline, toline)
334 return util.processlinerange(fromline, toline)
335 except error.ParseError as exc:
335 except error.ParseError as exc:
336 raise ErrorResponse(HTTP_BAD_REQUEST, str(exc))
336 raise ErrorResponse(HTTP_BAD_REQUEST, str(exc))
337
337
338 def formatlinerange(fromline, toline):
338 def formatlinerange(fromline, toline):
339 return '%d:%d' % (fromline + 1, toline)
339 return '%d:%d' % (fromline + 1, toline)
340
340
341 def commonentry(repo, ctx):
341 def commonentry(repo, ctx):
342 node = ctx.node()
342 node = ctx.node()
343 return {
343 return {
344 'rev': ctx.rev(),
344 'rev': ctx.rev(),
345 'node': hex(node),
345 'node': hex(node),
346 'author': ctx.user(),
346 'author': ctx.user(),
347 'desc': ctx.description(),
347 'desc': ctx.description(),
348 'date': ctx.date(),
348 'date': ctx.date(),
349 'extra': ctx.extra(),
349 'extra': ctx.extra(),
350 'phase': ctx.phasestr(),
350 'phase': ctx.phasestr(),
351 'branch': nodebranchnodefault(ctx),
351 'branch': nodebranchnodefault(ctx),
352 'inbranch': nodeinbranch(repo, ctx),
352 'inbranch': nodeinbranch(repo, ctx),
353 'branches': nodebranchdict(repo, ctx),
353 'branches': nodebranchdict(repo, ctx),
354 'tags': nodetagsdict(repo, node),
354 'tags': nodetagsdict(repo, node),
355 'bookmarks': nodebookmarksdict(repo, node),
355 'bookmarks': nodebookmarksdict(repo, node),
356 'parent': lambda **x: parents(ctx),
356 'parent': lambda **x: parents(ctx),
357 'child': lambda **x: children(ctx),
357 'child': lambda **x: children(ctx),
358 }
358 }
359
359
360 def changelistentry(web, ctx, tmpl):
360 def changelistentry(web, ctx, tmpl):
361 '''Obtain a dictionary to be used for entries in a changelist.
361 '''Obtain a dictionary to be used for entries in a changelist.
362
362
363 This function is called when producing items for the "entries" list passed
363 This function is called when producing items for the "entries" list passed
364 to the "shortlog" and "changelog" templates.
364 to the "shortlog" and "changelog" templates.
365 '''
365 '''
366 repo = web.repo
366 repo = web.repo
367 rev = ctx.rev()
367 rev = ctx.rev()
368 n = ctx.node()
368 n = ctx.node()
369 showtags = showtag(repo, tmpl, 'changelogtag', n)
369 showtags = showtag(repo, tmpl, 'changelogtag', n)
370 files = listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
370 files = listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
371
371
372 entry = commonentry(repo, ctx)
372 entry = commonentry(repo, ctx)
373 entry.update(
373 entry.update(
374 allparents=lambda **x: parents(ctx),
374 allparents=lambda **x: parents(ctx),
375 parent=lambda **x: parents(ctx, rev - 1),
375 parent=lambda **x: parents(ctx, rev - 1),
376 child=lambda **x: children(ctx, rev + 1),
376 child=lambda **x: children(ctx, rev + 1),
377 changelogtag=showtags,
377 changelogtag=showtags,
378 files=files,
378 files=files,
379 )
379 )
380 return entry
380 return entry
381
381
382 def symrevorshortnode(req, ctx):
382 def symrevorshortnode(req, ctx):
383 if 'node' in req.form:
383 if 'node' in req.form:
384 return templatefilters.revescape(req.form['node'][0])
384 return templatefilters.revescape(req.form['node'][0])
385 else:
385 else:
386 return short(ctx.node())
386 return short(ctx.node())
387
387
388 def changesetentry(web, req, tmpl, ctx):
388 def changesetentry(web, req, tmpl, ctx):
389 '''Obtain a dictionary to be used to render the "changeset" template.'''
389 '''Obtain a dictionary to be used to render the "changeset" template.'''
390
390
391 showtags = showtag(web.repo, tmpl, 'changesettag', ctx.node())
391 showtags = showtag(web.repo, tmpl, 'changesettag', ctx.node())
392 showbookmarks = showbookmark(web.repo, tmpl, 'changesetbookmark',
392 showbookmarks = showbookmark(web.repo, tmpl, 'changesetbookmark',
393 ctx.node())
393 ctx.node())
394 showbranch = nodebranchnodefault(ctx)
394 showbranch = nodebranchnodefault(ctx)
395
395
396 files = []
396 files = []
397 parity = paritygen(web.stripecount)
397 parity = paritygen(web.stripecount)
398 for blockno, f in enumerate(ctx.files()):
398 for blockno, f in enumerate(ctx.files()):
399 template = f in ctx and 'filenodelink' or 'filenolink'
399 template = f in ctx and 'filenodelink' or 'filenolink'
400 files.append(tmpl(template,
400 files.append(tmpl(template,
401 node=ctx.hex(), file=f, blockno=blockno + 1,
401 node=ctx.hex(), file=f, blockno=blockno + 1,
402 parity=next(parity)))
402 parity=next(parity)))
403
403
404 basectx = basechangectx(web.repo, req)
404 basectx = basechangectx(web.repo, req)
405 if basectx is None:
405 if basectx is None:
406 basectx = ctx.p1()
406 basectx = ctx.p1()
407
407
408 style = web.config('web', 'style', 'paper')
408 style = web.config('web', 'style', 'paper')
409 if 'style' in req.form:
409 if 'style' in req.form:
410 style = req.form['style'][0]
410 style = req.form['style'][0]
411
411
412 diff = diffs(web, tmpl, ctx, basectx, None, style)
412 diff = diffs(web, tmpl, ctx, basectx, None, style)
413
413
414 parity = paritygen(web.stripecount)
414 parity = paritygen(web.stripecount)
415 diffstatsgen = diffstatgen(ctx, basectx)
415 diffstatsgen = diffstatgen(ctx, basectx)
416 diffstats = diffstat(tmpl, ctx, diffstatsgen, parity)
416 diffstats = diffstat(tmpl, ctx, diffstatsgen, parity)
417
417
418 return dict(
418 return dict(
419 diff=diff,
419 diff=diff,
420 symrev=symrevorshortnode(req, ctx),
420 symrev=symrevorshortnode(req, ctx),
421 basenode=basectx.hex(),
421 basenode=basectx.hex(),
422 changesettag=showtags,
422 changesettag=showtags,
423 changesetbookmark=showbookmarks,
423 changesetbookmark=showbookmarks,
424 changesetbranch=showbranch,
424 changesetbranch=showbranch,
425 files=files,
425 files=files,
426 diffsummary=lambda **x: diffsummary(diffstatsgen),
426 diffsummary=lambda **x: diffsummary(diffstatsgen),
427 diffstat=diffstats,
427 diffstat=diffstats,
428 archives=web.archivelist(ctx.hex()),
428 archives=web.archivelist(ctx.hex()),
429 **commonentry(web.repo, ctx))
429 **commonentry(web.repo, ctx))
430
430
431 def listfilediffs(tmpl, files, node, max):
431 def listfilediffs(tmpl, files, node, max):
432 for f in files[:max]:
432 for f in files[:max]:
433 yield tmpl('filedifflink', node=hex(node), file=f)
433 yield tmpl('filedifflink', node=hex(node), file=f)
434 if len(files) > max:
434 if len(files) > max:
435 yield tmpl('fileellipses')
435 yield tmpl('fileellipses')
436
436
437 def diffs(web, tmpl, ctx, basectx, files, style, linerange=None):
437 def diffs(web, tmpl, ctx, basectx, files, style, linerange=None,
438 lineidprefix=''):
438
439
439 def prettyprintlines(lines, blockno):
440 def prettyprintlines(lines, blockno):
440 for lineno, l in enumerate(lines, 1):
441 for lineno, l in enumerate(lines, 1):
441 difflineno = "%d.%d" % (blockno, lineno)
442 difflineno = "%d.%d" % (blockno, lineno)
442 if l.startswith('+'):
443 if l.startswith('+'):
443 ltype = "difflineplus"
444 ltype = "difflineplus"
444 elif l.startswith('-'):
445 elif l.startswith('-'):
445 ltype = "difflineminus"
446 ltype = "difflineminus"
446 elif l.startswith('@'):
447 elif l.startswith('@'):
447 ltype = "difflineat"
448 ltype = "difflineat"
448 else:
449 else:
449 ltype = "diffline"
450 ltype = "diffline"
450 yield tmpl(ltype,
451 yield tmpl(ltype,
451 line=l,
452 line=l,
452 lineno=lineno,
453 lineno=lineno,
453 lineid="l%s" % difflineno,
454 lineid=lineidprefix + "l%s" % difflineno,
454 linenumber="% 8s" % difflineno)
455 linenumber="% 8s" % difflineno)
455
456
456 repo = web.repo
457 repo = web.repo
457 if files:
458 if files:
458 m = match.exact(repo.root, repo.getcwd(), files)
459 m = match.exact(repo.root, repo.getcwd(), files)
459 else:
460 else:
460 m = match.always(repo.root, repo.getcwd())
461 m = match.always(repo.root, repo.getcwd())
461
462
462 diffopts = patch.diffopts(repo.ui, untrusted=True)
463 diffopts = patch.diffopts(repo.ui, untrusted=True)
463 node1 = basectx.node()
464 node1 = basectx.node()
464 node2 = ctx.node()
465 node2 = ctx.node()
465 parity = paritygen(web.stripecount)
466 parity = paritygen(web.stripecount)
466
467
467 diffhunks = patch.diffhunks(repo, node1, node2, m, opts=diffopts)
468 diffhunks = patch.diffhunks(repo, node1, node2, m, opts=diffopts)
468 for blockno, (header, hunks) in enumerate(diffhunks, 1):
469 for blockno, (header, hunks) in enumerate(diffhunks, 1):
469 if style != 'raw':
470 if style != 'raw':
470 header = header[1:]
471 header = header[1:]
471 lines = [h + '\n' for h in header]
472 lines = [h + '\n' for h in header]
472 for hunkrange, hunklines in hunks:
473 for hunkrange, hunklines in hunks:
473 if linerange is not None and hunkrange is not None:
474 if linerange is not None and hunkrange is not None:
474 s1, l1, s2, l2 = hunkrange
475 s1, l1, s2, l2 = hunkrange
475 lb, ub = linerange
476 lb, ub = linerange
476 if not (lb < s2 + l2 and ub > s2):
477 if not (lb < s2 + l2 and ub > s2):
477 continue
478 continue
478 lines.extend(hunklines)
479 lines.extend(hunklines)
479 if lines:
480 if lines:
480 yield tmpl('diffblock', parity=next(parity), blockno=blockno,
481 yield tmpl('diffblock', parity=next(parity), blockno=blockno,
481 lines=prettyprintlines(lines, blockno))
482 lines=prettyprintlines(lines, blockno))
482
483
483 def compare(tmpl, context, leftlines, rightlines):
484 def compare(tmpl, context, leftlines, rightlines):
484 '''Generator function that provides side-by-side comparison data.'''
485 '''Generator function that provides side-by-side comparison data.'''
485
486
486 def compline(type, leftlineno, leftline, rightlineno, rightline):
487 def compline(type, leftlineno, leftline, rightlineno, rightline):
487 lineid = leftlineno and ("l%s" % leftlineno) or ''
488 lineid = leftlineno and ("l%s" % leftlineno) or ''
488 lineid += rightlineno and ("r%s" % rightlineno) or ''
489 lineid += rightlineno and ("r%s" % rightlineno) or ''
489 return tmpl('comparisonline',
490 return tmpl('comparisonline',
490 type=type,
491 type=type,
491 lineid=lineid,
492 lineid=lineid,
492 leftlineno=leftlineno,
493 leftlineno=leftlineno,
493 leftlinenumber="% 6s" % (leftlineno or ''),
494 leftlinenumber="% 6s" % (leftlineno or ''),
494 leftline=leftline or '',
495 leftline=leftline or '',
495 rightlineno=rightlineno,
496 rightlineno=rightlineno,
496 rightlinenumber="% 6s" % (rightlineno or ''),
497 rightlinenumber="% 6s" % (rightlineno or ''),
497 rightline=rightline or '')
498 rightline=rightline or '')
498
499
499 def getblock(opcodes):
500 def getblock(opcodes):
500 for type, llo, lhi, rlo, rhi in opcodes:
501 for type, llo, lhi, rlo, rhi in opcodes:
501 len1 = lhi - llo
502 len1 = lhi - llo
502 len2 = rhi - rlo
503 len2 = rhi - rlo
503 count = min(len1, len2)
504 count = min(len1, len2)
504 for i in xrange(count):
505 for i in xrange(count):
505 yield compline(type=type,
506 yield compline(type=type,
506 leftlineno=llo + i + 1,
507 leftlineno=llo + i + 1,
507 leftline=leftlines[llo + i],
508 leftline=leftlines[llo + i],
508 rightlineno=rlo + i + 1,
509 rightlineno=rlo + i + 1,
509 rightline=rightlines[rlo + i])
510 rightline=rightlines[rlo + i])
510 if len1 > len2:
511 if len1 > len2:
511 for i in xrange(llo + count, lhi):
512 for i in xrange(llo + count, lhi):
512 yield compline(type=type,
513 yield compline(type=type,
513 leftlineno=i + 1,
514 leftlineno=i + 1,
514 leftline=leftlines[i],
515 leftline=leftlines[i],
515 rightlineno=None,
516 rightlineno=None,
516 rightline=None)
517 rightline=None)
517 elif len2 > len1:
518 elif len2 > len1:
518 for i in xrange(rlo + count, rhi):
519 for i in xrange(rlo + count, rhi):
519 yield compline(type=type,
520 yield compline(type=type,
520 leftlineno=None,
521 leftlineno=None,
521 leftline=None,
522 leftline=None,
522 rightlineno=i + 1,
523 rightlineno=i + 1,
523 rightline=rightlines[i])
524 rightline=rightlines[i])
524
525
525 s = difflib.SequenceMatcher(None, leftlines, rightlines)
526 s = difflib.SequenceMatcher(None, leftlines, rightlines)
526 if context < 0:
527 if context < 0:
527 yield tmpl('comparisonblock', lines=getblock(s.get_opcodes()))
528 yield tmpl('comparisonblock', lines=getblock(s.get_opcodes()))
528 else:
529 else:
529 for oc in s.get_grouped_opcodes(n=context):
530 for oc in s.get_grouped_opcodes(n=context):
530 yield tmpl('comparisonblock', lines=getblock(oc))
531 yield tmpl('comparisonblock', lines=getblock(oc))
531
532
532 def diffstatgen(ctx, basectx):
533 def diffstatgen(ctx, basectx):
533 '''Generator function that provides the diffstat data.'''
534 '''Generator function that provides the diffstat data.'''
534
535
535 stats = patch.diffstatdata(util.iterlines(ctx.diff(basectx)))
536 stats = patch.diffstatdata(util.iterlines(ctx.diff(basectx)))
536 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
537 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
537 while True:
538 while True:
538 yield stats, maxname, maxtotal, addtotal, removetotal, binary
539 yield stats, maxname, maxtotal, addtotal, removetotal, binary
539
540
540 def diffsummary(statgen):
541 def diffsummary(statgen):
541 '''Return a short summary of the diff.'''
542 '''Return a short summary of the diff.'''
542
543
543 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
544 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
544 return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % (
545 return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % (
545 len(stats), addtotal, removetotal)
546 len(stats), addtotal, removetotal)
546
547
547 def diffstat(tmpl, ctx, statgen, parity):
548 def diffstat(tmpl, ctx, statgen, parity):
548 '''Return a diffstat template for each file in the diff.'''
549 '''Return a diffstat template for each file in the diff.'''
549
550
550 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
551 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
551 files = ctx.files()
552 files = ctx.files()
552
553
553 def pct(i):
554 def pct(i):
554 if maxtotal == 0:
555 if maxtotal == 0:
555 return 0
556 return 0
556 return (float(i) / maxtotal) * 100
557 return (float(i) / maxtotal) * 100
557
558
558 fileno = 0
559 fileno = 0
559 for filename, adds, removes, isbinary in stats:
560 for filename, adds, removes, isbinary in stats:
560 template = filename in files and 'diffstatlink' or 'diffstatnolink'
561 template = filename in files and 'diffstatlink' or 'diffstatnolink'
561 total = adds + removes
562 total = adds + removes
562 fileno += 1
563 fileno += 1
563 yield tmpl(template, node=ctx.hex(), file=filename, fileno=fileno,
564 yield tmpl(template, node=ctx.hex(), file=filename, fileno=fileno,
564 total=total, addpct=pct(adds), removepct=pct(removes),
565 total=total, addpct=pct(adds), removepct=pct(removes),
565 parity=next(parity))
566 parity=next(parity))
566
567
567 class sessionvars(object):
568 class sessionvars(object):
568 def __init__(self, vars, start='?'):
569 def __init__(self, vars, start='?'):
569 self.start = start
570 self.start = start
570 self.vars = vars
571 self.vars = vars
571 def __getitem__(self, key):
572 def __getitem__(self, key):
572 return self.vars[key]
573 return self.vars[key]
573 def __setitem__(self, key, value):
574 def __setitem__(self, key, value):
574 self.vars[key] = value
575 self.vars[key] = value
575 def __copy__(self):
576 def __copy__(self):
576 return sessionvars(copy.copy(self.vars), self.start)
577 return sessionvars(copy.copy(self.vars), self.start)
577 def __iter__(self):
578 def __iter__(self):
578 separator = self.start
579 separator = self.start
579 for key, value in sorted(self.vars.iteritems()):
580 for key, value in sorted(self.vars.iteritems()):
580 yield {'name': key, 'value': str(value), 'separator': separator}
581 yield {'name': key, 'value': str(value), 'separator': separator}
581 separator = '&'
582 separator = '&'
582
583
583 class wsgiui(uimod.ui):
584 class wsgiui(uimod.ui):
584 # default termwidth breaks under mod_wsgi
585 # default termwidth breaks under mod_wsgi
585 def termwidth(self):
586 def termwidth(self):
586 return 80
587 return 80
587
588
588 def getwebsubs(repo):
589 def getwebsubs(repo):
589 websubtable = []
590 websubtable = []
590 websubdefs = repo.ui.configitems('websub')
591 websubdefs = repo.ui.configitems('websub')
591 # we must maintain interhg backwards compatibility
592 # we must maintain interhg backwards compatibility
592 websubdefs += repo.ui.configitems('interhg')
593 websubdefs += repo.ui.configitems('interhg')
593 for key, pattern in websubdefs:
594 for key, pattern in websubdefs:
594 # grab the delimiter from the character after the "s"
595 # grab the delimiter from the character after the "s"
595 unesc = pattern[1]
596 unesc = pattern[1]
596 delim = re.escape(unesc)
597 delim = re.escape(unesc)
597
598
598 # identify portions of the pattern, taking care to avoid escaped
599 # identify portions of the pattern, taking care to avoid escaped
599 # delimiters. the replace format and flags are optional, but
600 # delimiters. the replace format and flags are optional, but
600 # delimiters are required.
601 # delimiters are required.
601 match = re.match(
602 match = re.match(
602 r'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$'
603 r'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$'
603 % (delim, delim, delim), pattern)
604 % (delim, delim, delim), pattern)
604 if not match:
605 if not match:
605 repo.ui.warn(_("websub: invalid pattern for %s: %s\n")
606 repo.ui.warn(_("websub: invalid pattern for %s: %s\n")
606 % (key, pattern))
607 % (key, pattern))
607 continue
608 continue
608
609
609 # we need to unescape the delimiter for regexp and format
610 # we need to unescape the delimiter for regexp and format
610 delim_re = re.compile(r'(?<!\\)\\%s' % delim)
611 delim_re = re.compile(r'(?<!\\)\\%s' % delim)
611 regexp = delim_re.sub(unesc, match.group(1))
612 regexp = delim_re.sub(unesc, match.group(1))
612 format = delim_re.sub(unesc, match.group(2))
613 format = delim_re.sub(unesc, match.group(2))
613
614
614 # the pattern allows for 6 regexp flags, so set them if necessary
615 # the pattern allows for 6 regexp flags, so set them if necessary
615 flagin = match.group(3)
616 flagin = match.group(3)
616 flags = 0
617 flags = 0
617 if flagin:
618 if flagin:
618 for flag in flagin.upper():
619 for flag in flagin.upper():
619 flags |= re.__dict__[flag]
620 flags |= re.__dict__[flag]
620
621
621 try:
622 try:
622 regexp = re.compile(regexp, flags)
623 regexp = re.compile(regexp, flags)
623 websubtable.append((regexp, format))
624 websubtable.append((regexp, format))
624 except re.error:
625 except re.error:
625 repo.ui.warn(_("websub: invalid regexp for %s: %s\n")
626 repo.ui.warn(_("websub: invalid regexp for %s: %s\n")
626 % (key, regexp))
627 % (key, regexp))
627 return websubtable
628 return websubtable
@@ -1,1641 +1,1641 b''
1 #require serve
1 #require serve
2
2
3 $ hg init test
3 $ hg init test
4 $ cd test
4 $ cd test
5 $ echo b > b
5 $ echo b > b
6 $ hg ci -Am "b"
6 $ hg ci -Am "b"
7 adding b
7 adding b
8 $ echo a > a
8 $ echo a > a
9 $ hg ci -Am "first a"
9 $ hg ci -Am "first a"
10 adding a
10 adding a
11 $ hg tag -r 1 a-tag
11 $ hg tag -r 1 a-tag
12 $ hg bookmark -r 1 a-bookmark
12 $ hg bookmark -r 1 a-bookmark
13 $ hg rm a
13 $ hg rm a
14 $ hg ci -m "del a"
14 $ hg ci -m "del a"
15 $ hg branch a-branch
15 $ hg branch a-branch
16 marked working directory as branch a-branch
16 marked working directory as branch a-branch
17 (branches are permanent and global, did you want a bookmark?)
17 (branches are permanent and global, did you want a bookmark?)
18 $ echo b > a
18 $ echo b > a
19 $ hg ci -Am "second a"
19 $ hg ci -Am "second a"
20 adding a
20 adding a
21 $ hg rm a
21 $ hg rm a
22 $ hg ci -m "del2 a"
22 $ hg ci -m "del2 a"
23 $ hg mv b c
23 $ hg mv b c
24 $ hg ci -m "mv b"
24 $ hg ci -m "mv b"
25 $ echo c >> c
25 $ echo c >> c
26 $ hg ci -m "change c"
26 $ hg ci -m "change c"
27 $ hg log -p
27 $ hg log -p
28 changeset: 7:46c1a66bd8fc
28 changeset: 7:46c1a66bd8fc
29 branch: a-branch
29 branch: a-branch
30 tag: tip
30 tag: tip
31 user: test
31 user: test
32 date: Thu Jan 01 00:00:00 1970 +0000
32 date: Thu Jan 01 00:00:00 1970 +0000
33 summary: change c
33 summary: change c
34
34
35 diff -r c9637d3cc8ef -r 46c1a66bd8fc c
35 diff -r c9637d3cc8ef -r 46c1a66bd8fc c
36 --- a/c Thu Jan 01 00:00:00 1970 +0000
36 --- a/c Thu Jan 01 00:00:00 1970 +0000
37 +++ b/c Thu Jan 01 00:00:00 1970 +0000
37 +++ b/c Thu Jan 01 00:00:00 1970 +0000
38 @@ -1,1 +1,2 @@
38 @@ -1,1 +1,2 @@
39 b
39 b
40 +c
40 +c
41
41
42 changeset: 6:c9637d3cc8ef
42 changeset: 6:c9637d3cc8ef
43 branch: a-branch
43 branch: a-branch
44 user: test
44 user: test
45 date: Thu Jan 01 00:00:00 1970 +0000
45 date: Thu Jan 01 00:00:00 1970 +0000
46 summary: mv b
46 summary: mv b
47
47
48 diff -r 958bd88be4eb -r c9637d3cc8ef b
48 diff -r 958bd88be4eb -r c9637d3cc8ef b
49 --- a/b Thu Jan 01 00:00:00 1970 +0000
49 --- a/b Thu Jan 01 00:00:00 1970 +0000
50 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
50 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
51 @@ -1,1 +0,0 @@
51 @@ -1,1 +0,0 @@
52 -b
52 -b
53 diff -r 958bd88be4eb -r c9637d3cc8ef c
53 diff -r 958bd88be4eb -r c9637d3cc8ef c
54 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
54 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
55 +++ b/c Thu Jan 01 00:00:00 1970 +0000
55 +++ b/c Thu Jan 01 00:00:00 1970 +0000
56 @@ -0,0 +1,1 @@
56 @@ -0,0 +1,1 @@
57 +b
57 +b
58
58
59 changeset: 5:958bd88be4eb
59 changeset: 5:958bd88be4eb
60 branch: a-branch
60 branch: a-branch
61 user: test
61 user: test
62 date: Thu Jan 01 00:00:00 1970 +0000
62 date: Thu Jan 01 00:00:00 1970 +0000
63 summary: del2 a
63 summary: del2 a
64
64
65 diff -r 3f41bc784e7e -r 958bd88be4eb a
65 diff -r 3f41bc784e7e -r 958bd88be4eb a
66 --- a/a Thu Jan 01 00:00:00 1970 +0000
66 --- a/a Thu Jan 01 00:00:00 1970 +0000
67 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
67 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
68 @@ -1,1 +0,0 @@
68 @@ -1,1 +0,0 @@
69 -b
69 -b
70
70
71 changeset: 4:3f41bc784e7e
71 changeset: 4:3f41bc784e7e
72 branch: a-branch
72 branch: a-branch
73 user: test
73 user: test
74 date: Thu Jan 01 00:00:00 1970 +0000
74 date: Thu Jan 01 00:00:00 1970 +0000
75 summary: second a
75 summary: second a
76
76
77 diff -r 292258f86fdf -r 3f41bc784e7e a
77 diff -r 292258f86fdf -r 3f41bc784e7e a
78 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
78 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
79 +++ b/a Thu Jan 01 00:00:00 1970 +0000
79 +++ b/a Thu Jan 01 00:00:00 1970 +0000
80 @@ -0,0 +1,1 @@
80 @@ -0,0 +1,1 @@
81 +b
81 +b
82
82
83 changeset: 3:292258f86fdf
83 changeset: 3:292258f86fdf
84 user: test
84 user: test
85 date: Thu Jan 01 00:00:00 1970 +0000
85 date: Thu Jan 01 00:00:00 1970 +0000
86 summary: del a
86 summary: del a
87
87
88 diff -r 94c9dd5ca9b4 -r 292258f86fdf a
88 diff -r 94c9dd5ca9b4 -r 292258f86fdf a
89 --- a/a Thu Jan 01 00:00:00 1970 +0000
89 --- a/a Thu Jan 01 00:00:00 1970 +0000
90 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
90 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
91 @@ -1,1 +0,0 @@
91 @@ -1,1 +0,0 @@
92 -a
92 -a
93
93
94 changeset: 2:94c9dd5ca9b4
94 changeset: 2:94c9dd5ca9b4
95 user: test
95 user: test
96 date: Thu Jan 01 00:00:00 1970 +0000
96 date: Thu Jan 01 00:00:00 1970 +0000
97 summary: Added tag a-tag for changeset 5ed941583260
97 summary: Added tag a-tag for changeset 5ed941583260
98
98
99 diff -r 5ed941583260 -r 94c9dd5ca9b4 .hgtags
99 diff -r 5ed941583260 -r 94c9dd5ca9b4 .hgtags
100 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
100 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
101 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
101 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
102 @@ -0,0 +1,1 @@
102 @@ -0,0 +1,1 @@
103 +5ed941583260248620985524192fdc382ef57c36 a-tag
103 +5ed941583260248620985524192fdc382ef57c36 a-tag
104
104
105 changeset: 1:5ed941583260
105 changeset: 1:5ed941583260
106 bookmark: a-bookmark
106 bookmark: a-bookmark
107 tag: a-tag
107 tag: a-tag
108 user: test
108 user: test
109 date: Thu Jan 01 00:00:00 1970 +0000
109 date: Thu Jan 01 00:00:00 1970 +0000
110 summary: first a
110 summary: first a
111
111
112 diff -r 6563da9dcf87 -r 5ed941583260 a
112 diff -r 6563da9dcf87 -r 5ed941583260 a
113 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
113 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
114 +++ b/a Thu Jan 01 00:00:00 1970 +0000
114 +++ b/a Thu Jan 01 00:00:00 1970 +0000
115 @@ -0,0 +1,1 @@
115 @@ -0,0 +1,1 @@
116 +a
116 +a
117
117
118 changeset: 0:6563da9dcf87
118 changeset: 0:6563da9dcf87
119 user: test
119 user: test
120 date: Thu Jan 01 00:00:00 1970 +0000
120 date: Thu Jan 01 00:00:00 1970 +0000
121 summary: b
121 summary: b
122
122
123 diff -r 000000000000 -r 6563da9dcf87 b
123 diff -r 000000000000 -r 6563da9dcf87 b
124 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
124 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
125 +++ b/b Thu Jan 01 00:00:00 1970 +0000
125 +++ b/b Thu Jan 01 00:00:00 1970 +0000
126 @@ -0,0 +1,1 @@
126 @@ -0,0 +1,1 @@
127 +b
127 +b
128
128
129 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -E errors.log
129 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -E errors.log
130 $ cat hg.pid >> $DAEMON_PIDS
130 $ cat hg.pid >> $DAEMON_PIDS
131
131
132 tip - two revisions
132 tip - two revisions
133
133
134 $ (get-with-headers.py localhost:$HGPORT 'log/tip/a')
134 $ (get-with-headers.py localhost:$HGPORT 'log/tip/a')
135 200 Script output follows
135 200 Script output follows
136
136
137 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
137 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
138 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
138 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
139 <head>
139 <head>
140 <link rel="icon" href="/static/hgicon.png" type="image/png" />
140 <link rel="icon" href="/static/hgicon.png" type="image/png" />
141 <meta name="robots" content="index, nofollow" />
141 <meta name="robots" content="index, nofollow" />
142 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
142 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
143 <script type="text/javascript" src="/static/mercurial.js"></script>
143 <script type="text/javascript" src="/static/mercurial.js"></script>
144
144
145 <title>test: a history</title>
145 <title>test: a history</title>
146 <link rel="alternate" type="application/atom+xml"
146 <link rel="alternate" type="application/atom+xml"
147 href="/atom-log/tip/a" title="Atom feed for test:a" />
147 href="/atom-log/tip/a" title="Atom feed for test:a" />
148 <link rel="alternate" type="application/rss+xml"
148 <link rel="alternate" type="application/rss+xml"
149 href="/rss-log/tip/a" title="RSS feed for test:a" />
149 href="/rss-log/tip/a" title="RSS feed for test:a" />
150 </head>
150 </head>
151 <body>
151 <body>
152
152
153 <div class="container">
153 <div class="container">
154 <div class="menu">
154 <div class="menu">
155 <div class="logo">
155 <div class="logo">
156 <a href="https://mercurial-scm.org/">
156 <a href="https://mercurial-scm.org/">
157 <img src="/static/hglogo.png" alt="mercurial" /></a>
157 <img src="/static/hglogo.png" alt="mercurial" /></a>
158 </div>
158 </div>
159 <ul>
159 <ul>
160 <li><a href="/shortlog/tip">log</a></li>
160 <li><a href="/shortlog/tip">log</a></li>
161 <li><a href="/graph/tip">graph</a></li>
161 <li><a href="/graph/tip">graph</a></li>
162 <li><a href="/tags">tags</a></li>
162 <li><a href="/tags">tags</a></li>
163 <li><a href="/bookmarks">bookmarks</a></li>
163 <li><a href="/bookmarks">bookmarks</a></li>
164 <li><a href="/branches">branches</a></li>
164 <li><a href="/branches">branches</a></li>
165 </ul>
165 </ul>
166 <ul>
166 <ul>
167 <li><a href="/rev/tip">changeset</a></li>
167 <li><a href="/rev/tip">changeset</a></li>
168 <li><a href="/file/tip">browse</a></li>
168 <li><a href="/file/tip">browse</a></li>
169 </ul>
169 </ul>
170 <ul>
170 <ul>
171 <li><a href="/file/tip/a">file</a></li>
171 <li><a href="/file/tip/a">file</a></li>
172 <li><a href="/diff/tip/a">diff</a></li>
172 <li><a href="/diff/tip/a">diff</a></li>
173 <li><a href="/comparison/tip/a">comparison</a></li>
173 <li><a href="/comparison/tip/a">comparison</a></li>
174 <li><a href="/annotate/tip/a">annotate</a></li>
174 <li><a href="/annotate/tip/a">annotate</a></li>
175 <li class="active">file log</li>
175 <li class="active">file log</li>
176 <li><a href="/raw-file/tip/a">raw</a></li>
176 <li><a href="/raw-file/tip/a">raw</a></li>
177 </ul>
177 </ul>
178 <ul>
178 <ul>
179 <li><a href="/help">help</a></li>
179 <li><a href="/help">help</a></li>
180 </ul>
180 </ul>
181 <div class="atom-logo">
181 <div class="atom-logo">
182 <a href="/atom-log/tip/a" title="subscribe to atom feed">
182 <a href="/atom-log/tip/a" title="subscribe to atom feed">
183 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
183 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
184 </a>
184 </a>
185 </div>
185 </div>
186 </div>
186 </div>
187
187
188 <div class="main">
188 <div class="main">
189 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
189 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
190 <h3>
190 <h3>
191 log a @ 4:<a href="/rev/3f41bc784e7e">3f41bc784e7e</a>
191 log a @ 4:<a href="/rev/3f41bc784e7e">3f41bc784e7e</a>
192 <span class="branchname">a-branch</span>
192 <span class="branchname">a-branch</span>
193
193
194 </h3>
194 </h3>
195
195
196 <form class="search" action="/log">
196 <form class="search" action="/log">
197
197
198 <p><input name="rev" id="search1" type="text" size="30" /></p>
198 <p><input name="rev" id="search1" type="text" size="30" /></p>
199 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
199 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
200 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
200 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
201 </form>
201 </form>
202
202
203 <div class="navigate">
203 <div class="navigate">
204 <a href="/log/tip/a?revcount=30">less</a>
204 <a href="/log/tip/a?revcount=30">less</a>
205 <a href="/log/tip/a?revcount=120">more</a>
205 <a href="/log/tip/a?revcount=120">more</a>
206 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
206 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
207
207
208 <table class="bigtable">
208 <table class="bigtable">
209 <thead>
209 <thead>
210 <tr>
210 <tr>
211 <th class="age">age</th>
211 <th class="age">age</th>
212 <th class="author">author</th>
212 <th class="author">author</th>
213 <th class="description">description</th>
213 <th class="description">description</th>
214 </tr>
214 </tr>
215 </thead>
215 </thead>
216 <tbody class="stripes2">
216 <tbody class="stripes2">
217 <tr>
217 <tr>
218 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
218 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
219 <td class="author">test</td>
219 <td class="author">test</td>
220 <td class="description">
220 <td class="description">
221 <a href="/rev/3f41bc784e7e">second a</a>
221 <a href="/rev/3f41bc784e7e">second a</a>
222 <span class="branchname">a-branch</span>
222 <span class="branchname">a-branch</span>
223 </td>
223 </td>
224 </tr>
224 </tr>
225
225
226 <tr>
226 <tr>
227 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
227 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
228 <td class="author">test</td>
228 <td class="author">test</td>
229 <td class="description">
229 <td class="description">
230 <a href="/rev/5ed941583260">first a</a>
230 <a href="/rev/5ed941583260">first a</a>
231 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
231 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
232 </td>
232 </td>
233 </tr>
233 </tr>
234
234
235
235
236 </tbody>
236 </tbody>
237 </table>
237 </table>
238
238
239 <div class="navigate">
239 <div class="navigate">
240 <a href="/log/tip/a?revcount=30">less</a>
240 <a href="/log/tip/a?revcount=30">less</a>
241 <a href="/log/tip/a?revcount=120">more</a>
241 <a href="/log/tip/a?revcount=120">more</a>
242 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a>
242 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a>
243 </div>
243 </div>
244
244
245 </div>
245 </div>
246 </div>
246 </div>
247
247
248
248
249
249
250 </body>
250 </body>
251 </html>
251 </html>
252
252
253
253
254 second version - two revisions
254 second version - two revisions
255
255
256 $ (get-with-headers.py localhost:$HGPORT 'log/4/a')
256 $ (get-with-headers.py localhost:$HGPORT 'log/4/a')
257 200 Script output follows
257 200 Script output follows
258
258
259 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
259 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
260 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
260 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
261 <head>
261 <head>
262 <link rel="icon" href="/static/hgicon.png" type="image/png" />
262 <link rel="icon" href="/static/hgicon.png" type="image/png" />
263 <meta name="robots" content="index, nofollow" />
263 <meta name="robots" content="index, nofollow" />
264 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
264 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
265 <script type="text/javascript" src="/static/mercurial.js"></script>
265 <script type="text/javascript" src="/static/mercurial.js"></script>
266
266
267 <title>test: a history</title>
267 <title>test: a history</title>
268 <link rel="alternate" type="application/atom+xml"
268 <link rel="alternate" type="application/atom+xml"
269 href="/atom-log/tip/a" title="Atom feed for test:a" />
269 href="/atom-log/tip/a" title="Atom feed for test:a" />
270 <link rel="alternate" type="application/rss+xml"
270 <link rel="alternate" type="application/rss+xml"
271 href="/rss-log/tip/a" title="RSS feed for test:a" />
271 href="/rss-log/tip/a" title="RSS feed for test:a" />
272 </head>
272 </head>
273 <body>
273 <body>
274
274
275 <div class="container">
275 <div class="container">
276 <div class="menu">
276 <div class="menu">
277 <div class="logo">
277 <div class="logo">
278 <a href="https://mercurial-scm.org/">
278 <a href="https://mercurial-scm.org/">
279 <img src="/static/hglogo.png" alt="mercurial" /></a>
279 <img src="/static/hglogo.png" alt="mercurial" /></a>
280 </div>
280 </div>
281 <ul>
281 <ul>
282 <li><a href="/shortlog/4">log</a></li>
282 <li><a href="/shortlog/4">log</a></li>
283 <li><a href="/graph/4">graph</a></li>
283 <li><a href="/graph/4">graph</a></li>
284 <li><a href="/tags">tags</a></li>
284 <li><a href="/tags">tags</a></li>
285 <li><a href="/bookmarks">bookmarks</a></li>
285 <li><a href="/bookmarks">bookmarks</a></li>
286 <li><a href="/branches">branches</a></li>
286 <li><a href="/branches">branches</a></li>
287 </ul>
287 </ul>
288 <ul>
288 <ul>
289 <li><a href="/rev/4">changeset</a></li>
289 <li><a href="/rev/4">changeset</a></li>
290 <li><a href="/file/4">browse</a></li>
290 <li><a href="/file/4">browse</a></li>
291 </ul>
291 </ul>
292 <ul>
292 <ul>
293 <li><a href="/file/4/a">file</a></li>
293 <li><a href="/file/4/a">file</a></li>
294 <li><a href="/diff/4/a">diff</a></li>
294 <li><a href="/diff/4/a">diff</a></li>
295 <li><a href="/comparison/4/a">comparison</a></li>
295 <li><a href="/comparison/4/a">comparison</a></li>
296 <li><a href="/annotate/4/a">annotate</a></li>
296 <li><a href="/annotate/4/a">annotate</a></li>
297 <li class="active">file log</li>
297 <li class="active">file log</li>
298 <li><a href="/raw-file/4/a">raw</a></li>
298 <li><a href="/raw-file/4/a">raw</a></li>
299 </ul>
299 </ul>
300 <ul>
300 <ul>
301 <li><a href="/help">help</a></li>
301 <li><a href="/help">help</a></li>
302 </ul>
302 </ul>
303 <div class="atom-logo">
303 <div class="atom-logo">
304 <a href="/atom-log/tip/a" title="subscribe to atom feed">
304 <a href="/atom-log/tip/a" title="subscribe to atom feed">
305 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
305 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
306 </a>
306 </a>
307 </div>
307 </div>
308 </div>
308 </div>
309
309
310 <div class="main">
310 <div class="main">
311 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
311 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
312 <h3>
312 <h3>
313 log a @ 4:<a href="/rev/3f41bc784e7e">3f41bc784e7e</a>
313 log a @ 4:<a href="/rev/3f41bc784e7e">3f41bc784e7e</a>
314 <span class="branchname">a-branch</span>
314 <span class="branchname">a-branch</span>
315
315
316 </h3>
316 </h3>
317
317
318 <form class="search" action="/log">
318 <form class="search" action="/log">
319
319
320 <p><input name="rev" id="search1" type="text" size="30" /></p>
320 <p><input name="rev" id="search1" type="text" size="30" /></p>
321 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
321 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
322 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
322 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
323 </form>
323 </form>
324
324
325 <div class="navigate">
325 <div class="navigate">
326 <a href="/log/4/a?revcount=30">less</a>
326 <a href="/log/4/a?revcount=30">less</a>
327 <a href="/log/4/a?revcount=120">more</a>
327 <a href="/log/4/a?revcount=120">more</a>
328 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
328 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
329
329
330 <table class="bigtable">
330 <table class="bigtable">
331 <thead>
331 <thead>
332 <tr>
332 <tr>
333 <th class="age">age</th>
333 <th class="age">age</th>
334 <th class="author">author</th>
334 <th class="author">author</th>
335 <th class="description">description</th>
335 <th class="description">description</th>
336 </tr>
336 </tr>
337 </thead>
337 </thead>
338 <tbody class="stripes2">
338 <tbody class="stripes2">
339 <tr>
339 <tr>
340 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
340 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
341 <td class="author">test</td>
341 <td class="author">test</td>
342 <td class="description">
342 <td class="description">
343 <a href="/rev/3f41bc784e7e">second a</a>
343 <a href="/rev/3f41bc784e7e">second a</a>
344 <span class="branchname">a-branch</span>
344 <span class="branchname">a-branch</span>
345 </td>
345 </td>
346 </tr>
346 </tr>
347
347
348 <tr>
348 <tr>
349 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
349 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
350 <td class="author">test</td>
350 <td class="author">test</td>
351 <td class="description">
351 <td class="description">
352 <a href="/rev/5ed941583260">first a</a>
352 <a href="/rev/5ed941583260">first a</a>
353 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
353 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
354 </td>
354 </td>
355 </tr>
355 </tr>
356
356
357
357
358 </tbody>
358 </tbody>
359 </table>
359 </table>
360
360
361 <div class="navigate">
361 <div class="navigate">
362 <a href="/log/4/a?revcount=30">less</a>
362 <a href="/log/4/a?revcount=30">less</a>
363 <a href="/log/4/a?revcount=120">more</a>
363 <a href="/log/4/a?revcount=120">more</a>
364 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a>
364 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a>
365 </div>
365 </div>
366
366
367 </div>
367 </div>
368 </div>
368 </div>
369
369
370
370
371
371
372 </body>
372 </body>
373 </html>
373 </html>
374
374
375
375
376 first deleted - one revision
376 first deleted - one revision
377
377
378 $ (get-with-headers.py localhost:$HGPORT 'log/3/a')
378 $ (get-with-headers.py localhost:$HGPORT 'log/3/a')
379 200 Script output follows
379 200 Script output follows
380
380
381 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
381 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
382 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
382 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
383 <head>
383 <head>
384 <link rel="icon" href="/static/hgicon.png" type="image/png" />
384 <link rel="icon" href="/static/hgicon.png" type="image/png" />
385 <meta name="robots" content="index, nofollow" />
385 <meta name="robots" content="index, nofollow" />
386 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
386 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
387 <script type="text/javascript" src="/static/mercurial.js"></script>
387 <script type="text/javascript" src="/static/mercurial.js"></script>
388
388
389 <title>test: a history</title>
389 <title>test: a history</title>
390 <link rel="alternate" type="application/atom+xml"
390 <link rel="alternate" type="application/atom+xml"
391 href="/atom-log/tip/a" title="Atom feed for test:a" />
391 href="/atom-log/tip/a" title="Atom feed for test:a" />
392 <link rel="alternate" type="application/rss+xml"
392 <link rel="alternate" type="application/rss+xml"
393 href="/rss-log/tip/a" title="RSS feed for test:a" />
393 href="/rss-log/tip/a" title="RSS feed for test:a" />
394 </head>
394 </head>
395 <body>
395 <body>
396
396
397 <div class="container">
397 <div class="container">
398 <div class="menu">
398 <div class="menu">
399 <div class="logo">
399 <div class="logo">
400 <a href="https://mercurial-scm.org/">
400 <a href="https://mercurial-scm.org/">
401 <img src="/static/hglogo.png" alt="mercurial" /></a>
401 <img src="/static/hglogo.png" alt="mercurial" /></a>
402 </div>
402 </div>
403 <ul>
403 <ul>
404 <li><a href="/shortlog/3">log</a></li>
404 <li><a href="/shortlog/3">log</a></li>
405 <li><a href="/graph/3">graph</a></li>
405 <li><a href="/graph/3">graph</a></li>
406 <li><a href="/tags">tags</a></li>
406 <li><a href="/tags">tags</a></li>
407 <li><a href="/bookmarks">bookmarks</a></li>
407 <li><a href="/bookmarks">bookmarks</a></li>
408 <li><a href="/branches">branches</a></li>
408 <li><a href="/branches">branches</a></li>
409 </ul>
409 </ul>
410 <ul>
410 <ul>
411 <li><a href="/rev/3">changeset</a></li>
411 <li><a href="/rev/3">changeset</a></li>
412 <li><a href="/file/3">browse</a></li>
412 <li><a href="/file/3">browse</a></li>
413 </ul>
413 </ul>
414 <ul>
414 <ul>
415 <li><a href="/file/3/a">file</a></li>
415 <li><a href="/file/3/a">file</a></li>
416 <li><a href="/diff/3/a">diff</a></li>
416 <li><a href="/diff/3/a">diff</a></li>
417 <li><a href="/comparison/3/a">comparison</a></li>
417 <li><a href="/comparison/3/a">comparison</a></li>
418 <li><a href="/annotate/3/a">annotate</a></li>
418 <li><a href="/annotate/3/a">annotate</a></li>
419 <li class="active">file log</li>
419 <li class="active">file log</li>
420 <li><a href="/raw-file/3/a">raw</a></li>
420 <li><a href="/raw-file/3/a">raw</a></li>
421 </ul>
421 </ul>
422 <ul>
422 <ul>
423 <li><a href="/help">help</a></li>
423 <li><a href="/help">help</a></li>
424 </ul>
424 </ul>
425 <div class="atom-logo">
425 <div class="atom-logo">
426 <a href="/atom-log/tip/a" title="subscribe to atom feed">
426 <a href="/atom-log/tip/a" title="subscribe to atom feed">
427 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
427 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
428 </a>
428 </a>
429 </div>
429 </div>
430 </div>
430 </div>
431
431
432 <div class="main">
432 <div class="main">
433 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
433 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
434 <h3>
434 <h3>
435 log a @ 1:<a href="/rev/5ed941583260">5ed941583260</a>
435 log a @ 1:<a href="/rev/5ed941583260">5ed941583260</a>
436 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
436 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
437
437
438 </h3>
438 </h3>
439
439
440 <form class="search" action="/log">
440 <form class="search" action="/log">
441
441
442 <p><input name="rev" id="search1" type="text" size="30" /></p>
442 <p><input name="rev" id="search1" type="text" size="30" /></p>
443 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
443 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
444 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
444 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
445 </form>
445 </form>
446
446
447 <div class="navigate">
447 <div class="navigate">
448 <a href="/log/3/a?revcount=30">less</a>
448 <a href="/log/3/a?revcount=30">less</a>
449 <a href="/log/3/a?revcount=120">more</a>
449 <a href="/log/3/a?revcount=120">more</a>
450 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
450 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
451
451
452 <table class="bigtable">
452 <table class="bigtable">
453 <thead>
453 <thead>
454 <tr>
454 <tr>
455 <th class="age">age</th>
455 <th class="age">age</th>
456 <th class="author">author</th>
456 <th class="author">author</th>
457 <th class="description">description</th>
457 <th class="description">description</th>
458 </tr>
458 </tr>
459 </thead>
459 </thead>
460 <tbody class="stripes2">
460 <tbody class="stripes2">
461 <tr>
461 <tr>
462 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
462 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
463 <td class="author">test</td>
463 <td class="author">test</td>
464 <td class="description">
464 <td class="description">
465 <a href="/rev/5ed941583260">first a</a>
465 <a href="/rev/5ed941583260">first a</a>
466 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
466 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
467 </td>
467 </td>
468 </tr>
468 </tr>
469
469
470
470
471 </tbody>
471 </tbody>
472 </table>
472 </table>
473
473
474 <div class="navigate">
474 <div class="navigate">
475 <a href="/log/3/a?revcount=30">less</a>
475 <a href="/log/3/a?revcount=30">less</a>
476 <a href="/log/3/a?revcount=120">more</a>
476 <a href="/log/3/a?revcount=120">more</a>
477 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a>
477 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a>
478 </div>
478 </div>
479
479
480 </div>
480 </div>
481 </div>
481 </div>
482
482
483
483
484
484
485 </body>
485 </body>
486 </html>
486 </html>
487
487
488
488
489 first version - one revision
489 first version - one revision
490
490
491 $ (get-with-headers.py localhost:$HGPORT 'log/1/a')
491 $ (get-with-headers.py localhost:$HGPORT 'log/1/a')
492 200 Script output follows
492 200 Script output follows
493
493
494 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
494 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
495 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
495 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
496 <head>
496 <head>
497 <link rel="icon" href="/static/hgicon.png" type="image/png" />
497 <link rel="icon" href="/static/hgicon.png" type="image/png" />
498 <meta name="robots" content="index, nofollow" />
498 <meta name="robots" content="index, nofollow" />
499 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
499 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
500 <script type="text/javascript" src="/static/mercurial.js"></script>
500 <script type="text/javascript" src="/static/mercurial.js"></script>
501
501
502 <title>test: a history</title>
502 <title>test: a history</title>
503 <link rel="alternate" type="application/atom+xml"
503 <link rel="alternate" type="application/atom+xml"
504 href="/atom-log/tip/a" title="Atom feed for test:a" />
504 href="/atom-log/tip/a" title="Atom feed for test:a" />
505 <link rel="alternate" type="application/rss+xml"
505 <link rel="alternate" type="application/rss+xml"
506 href="/rss-log/tip/a" title="RSS feed for test:a" />
506 href="/rss-log/tip/a" title="RSS feed for test:a" />
507 </head>
507 </head>
508 <body>
508 <body>
509
509
510 <div class="container">
510 <div class="container">
511 <div class="menu">
511 <div class="menu">
512 <div class="logo">
512 <div class="logo">
513 <a href="https://mercurial-scm.org/">
513 <a href="https://mercurial-scm.org/">
514 <img src="/static/hglogo.png" alt="mercurial" /></a>
514 <img src="/static/hglogo.png" alt="mercurial" /></a>
515 </div>
515 </div>
516 <ul>
516 <ul>
517 <li><a href="/shortlog/1">log</a></li>
517 <li><a href="/shortlog/1">log</a></li>
518 <li><a href="/graph/1">graph</a></li>
518 <li><a href="/graph/1">graph</a></li>
519 <li><a href="/tags">tags</a></li>
519 <li><a href="/tags">tags</a></li>
520 <li><a href="/bookmarks">bookmarks</a></li>
520 <li><a href="/bookmarks">bookmarks</a></li>
521 <li><a href="/branches">branches</a></li>
521 <li><a href="/branches">branches</a></li>
522 </ul>
522 </ul>
523 <ul>
523 <ul>
524 <li><a href="/rev/1">changeset</a></li>
524 <li><a href="/rev/1">changeset</a></li>
525 <li><a href="/file/1">browse</a></li>
525 <li><a href="/file/1">browse</a></li>
526 </ul>
526 </ul>
527 <ul>
527 <ul>
528 <li><a href="/file/1/a">file</a></li>
528 <li><a href="/file/1/a">file</a></li>
529 <li><a href="/diff/1/a">diff</a></li>
529 <li><a href="/diff/1/a">diff</a></li>
530 <li><a href="/comparison/1/a">comparison</a></li>
530 <li><a href="/comparison/1/a">comparison</a></li>
531 <li><a href="/annotate/1/a">annotate</a></li>
531 <li><a href="/annotate/1/a">annotate</a></li>
532 <li class="active">file log</li>
532 <li class="active">file log</li>
533 <li><a href="/raw-file/1/a">raw</a></li>
533 <li><a href="/raw-file/1/a">raw</a></li>
534 </ul>
534 </ul>
535 <ul>
535 <ul>
536 <li><a href="/help">help</a></li>
536 <li><a href="/help">help</a></li>
537 </ul>
537 </ul>
538 <div class="atom-logo">
538 <div class="atom-logo">
539 <a href="/atom-log/tip/a" title="subscribe to atom feed">
539 <a href="/atom-log/tip/a" title="subscribe to atom feed">
540 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
540 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
541 </a>
541 </a>
542 </div>
542 </div>
543 </div>
543 </div>
544
544
545 <div class="main">
545 <div class="main">
546 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
546 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
547 <h3>
547 <h3>
548 log a @ 1:<a href="/rev/5ed941583260">5ed941583260</a>
548 log a @ 1:<a href="/rev/5ed941583260">5ed941583260</a>
549 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
549 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
550
550
551 </h3>
551 </h3>
552
552
553 <form class="search" action="/log">
553 <form class="search" action="/log">
554
554
555 <p><input name="rev" id="search1" type="text" size="30" /></p>
555 <p><input name="rev" id="search1" type="text" size="30" /></p>
556 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
556 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
557 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
557 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
558 </form>
558 </form>
559
559
560 <div class="navigate">
560 <div class="navigate">
561 <a href="/log/1/a?revcount=30">less</a>
561 <a href="/log/1/a?revcount=30">less</a>
562 <a href="/log/1/a?revcount=120">more</a>
562 <a href="/log/1/a?revcount=120">more</a>
563 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
563 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
564
564
565 <table class="bigtable">
565 <table class="bigtable">
566 <thead>
566 <thead>
567 <tr>
567 <tr>
568 <th class="age">age</th>
568 <th class="age">age</th>
569 <th class="author">author</th>
569 <th class="author">author</th>
570 <th class="description">description</th>
570 <th class="description">description</th>
571 </tr>
571 </tr>
572 </thead>
572 </thead>
573 <tbody class="stripes2">
573 <tbody class="stripes2">
574 <tr>
574 <tr>
575 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
575 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
576 <td class="author">test</td>
576 <td class="author">test</td>
577 <td class="description">
577 <td class="description">
578 <a href="/rev/5ed941583260">first a</a>
578 <a href="/rev/5ed941583260">first a</a>
579 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
579 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
580 </td>
580 </td>
581 </tr>
581 </tr>
582
582
583
583
584 </tbody>
584 </tbody>
585 </table>
585 </table>
586
586
587 <div class="navigate">
587 <div class="navigate">
588 <a href="/log/1/a?revcount=30">less</a>
588 <a href="/log/1/a?revcount=30">less</a>
589 <a href="/log/1/a?revcount=120">more</a>
589 <a href="/log/1/a?revcount=120">more</a>
590 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a>
590 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a>
591 </div>
591 </div>
592
592
593 </div>
593 </div>
594 </div>
594 </div>
595
595
596
596
597
597
598 </body>
598 </body>
599 </html>
599 </html>
600
600
601
601
602 before addition - error
602 before addition - error
603
603
604 $ (get-with-headers.py localhost:$HGPORT 'log/0/a')
604 $ (get-with-headers.py localhost:$HGPORT 'log/0/a')
605 404 Not Found
605 404 Not Found
606
606
607 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
607 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
608 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
608 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
609 <head>
609 <head>
610 <link rel="icon" href="/static/hgicon.png" type="image/png" />
610 <link rel="icon" href="/static/hgicon.png" type="image/png" />
611 <meta name="robots" content="index, nofollow" />
611 <meta name="robots" content="index, nofollow" />
612 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
612 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
613 <script type="text/javascript" src="/static/mercurial.js"></script>
613 <script type="text/javascript" src="/static/mercurial.js"></script>
614
614
615 <title>test: error</title>
615 <title>test: error</title>
616 </head>
616 </head>
617 <body>
617 <body>
618
618
619 <div class="container">
619 <div class="container">
620 <div class="menu">
620 <div class="menu">
621 <div class="logo">
621 <div class="logo">
622 <a href="https://mercurial-scm.org/">
622 <a href="https://mercurial-scm.org/">
623 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
623 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
624 </div>
624 </div>
625 <ul>
625 <ul>
626 <li><a href="/shortlog">log</a></li>
626 <li><a href="/shortlog">log</a></li>
627 <li><a href="/graph">graph</a></li>
627 <li><a href="/graph">graph</a></li>
628 <li><a href="/tags">tags</a></li>
628 <li><a href="/tags">tags</a></li>
629 <li><a href="/bookmarks">bookmarks</a></li>
629 <li><a href="/bookmarks">bookmarks</a></li>
630 <li><a href="/branches">branches</a></li>
630 <li><a href="/branches">branches</a></li>
631 </ul>
631 </ul>
632 <ul>
632 <ul>
633 <li><a href="/help">help</a></li>
633 <li><a href="/help">help</a></li>
634 </ul>
634 </ul>
635 </div>
635 </div>
636
636
637 <div class="main">
637 <div class="main">
638
638
639 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
639 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
640 <h3>error</h3>
640 <h3>error</h3>
641
641
642 <form class="search" action="/log">
642 <form class="search" action="/log">
643
643
644 <p><input name="rev" id="search1" type="text" size="30"></p>
644 <p><input name="rev" id="search1" type="text" size="30"></p>
645 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
645 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
646 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
646 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
647 </form>
647 </form>
648
648
649 <div class="description">
649 <div class="description">
650 <p>
650 <p>
651 An error occurred while processing your request:
651 An error occurred while processing your request:
652 </p>
652 </p>
653 <p>
653 <p>
654 a@6563da9dcf87: not found in manifest
654 a@6563da9dcf87: not found in manifest
655 </p>
655 </p>
656 </div>
656 </div>
657 </div>
657 </div>
658 </div>
658 </div>
659
659
660
660
661
661
662 </body>
662 </body>
663 </html>
663 </html>
664
664
665 [1]
665 [1]
666
666
667 $ hg log -r 'followlines(c, 1:2, startrev=tip) and follow(c)'
667 $ hg log -r 'followlines(c, 1:2, startrev=tip) and follow(c)'
668 changeset: 0:6563da9dcf87
668 changeset: 0:6563da9dcf87
669 user: test
669 user: test
670 date: Thu Jan 01 00:00:00 1970 +0000
670 date: Thu Jan 01 00:00:00 1970 +0000
671 summary: b
671 summary: b
672
672
673 changeset: 7:46c1a66bd8fc
673 changeset: 7:46c1a66bd8fc
674 branch: a-branch
674 branch: a-branch
675 tag: tip
675 tag: tip
676 user: test
676 user: test
677 date: Thu Jan 01 00:00:00 1970 +0000
677 date: Thu Jan 01 00:00:00 1970 +0000
678 summary: change c
678 summary: change c
679
679
680 $ (get-with-headers.py localhost:$HGPORT 'log/tip/c?linerange=1:2')
680 $ (get-with-headers.py localhost:$HGPORT 'log/tip/c?linerange=1:2')
681 200 Script output follows
681 200 Script output follows
682
682
683 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
683 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
684 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
684 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
685 <head>
685 <head>
686 <link rel="icon" href="/static/hgicon.png" type="image/png" />
686 <link rel="icon" href="/static/hgicon.png" type="image/png" />
687 <meta name="robots" content="index, nofollow" />
687 <meta name="robots" content="index, nofollow" />
688 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
688 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
689 <script type="text/javascript" src="/static/mercurial.js"></script>
689 <script type="text/javascript" src="/static/mercurial.js"></script>
690
690
691 <title>test: c history</title>
691 <title>test: c history</title>
692 <link rel="alternate" type="application/atom+xml"
692 <link rel="alternate" type="application/atom+xml"
693 href="/atom-log/tip/c" title="Atom feed for test:c" />
693 href="/atom-log/tip/c" title="Atom feed for test:c" />
694 <link rel="alternate" type="application/rss+xml"
694 <link rel="alternate" type="application/rss+xml"
695 href="/rss-log/tip/c" title="RSS feed for test:c" />
695 href="/rss-log/tip/c" title="RSS feed for test:c" />
696 </head>
696 </head>
697 <body>
697 <body>
698
698
699 <div class="container">
699 <div class="container">
700 <div class="menu">
700 <div class="menu">
701 <div class="logo">
701 <div class="logo">
702 <a href="https://mercurial-scm.org/">
702 <a href="https://mercurial-scm.org/">
703 <img src="/static/hglogo.png" alt="mercurial" /></a>
703 <img src="/static/hglogo.png" alt="mercurial" /></a>
704 </div>
704 </div>
705 <ul>
705 <ul>
706 <li><a href="/shortlog/tip">log</a></li>
706 <li><a href="/shortlog/tip">log</a></li>
707 <li><a href="/graph/tip">graph</a></li>
707 <li><a href="/graph/tip">graph</a></li>
708 <li><a href="/tags">tags</a></li>
708 <li><a href="/tags">tags</a></li>
709 <li><a href="/bookmarks">bookmarks</a></li>
709 <li><a href="/bookmarks">bookmarks</a></li>
710 <li><a href="/branches">branches</a></li>
710 <li><a href="/branches">branches</a></li>
711 </ul>
711 </ul>
712 <ul>
712 <ul>
713 <li><a href="/rev/tip">changeset</a></li>
713 <li><a href="/rev/tip">changeset</a></li>
714 <li><a href="/file/tip">browse</a></li>
714 <li><a href="/file/tip">browse</a></li>
715 </ul>
715 </ul>
716 <ul>
716 <ul>
717 <li><a href="/file/tip/c">file</a></li>
717 <li><a href="/file/tip/c">file</a></li>
718 <li><a href="/diff/tip/c">diff</a></li>
718 <li><a href="/diff/tip/c">diff</a></li>
719 <li><a href="/comparison/tip/c">comparison</a></li>
719 <li><a href="/comparison/tip/c">comparison</a></li>
720 <li><a href="/annotate/tip/c">annotate</a></li>
720 <li><a href="/annotate/tip/c">annotate</a></li>
721 <li class="active">file log</li>
721 <li class="active">file log</li>
722 <li><a href="/raw-file/tip/c">raw</a></li>
722 <li><a href="/raw-file/tip/c">raw</a></li>
723 </ul>
723 </ul>
724 <ul>
724 <ul>
725 <li><a href="/help">help</a></li>
725 <li><a href="/help">help</a></li>
726 </ul>
726 </ul>
727 <div class="atom-logo">
727 <div class="atom-logo">
728 <a href="/atom-log/tip/c" title="subscribe to atom feed">
728 <a href="/atom-log/tip/c" title="subscribe to atom feed">
729 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
729 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
730 </a>
730 </a>
731 </div>
731 </div>
732 </div>
732 </div>
733
733
734 <div class="main">
734 <div class="main">
735 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
735 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
736 <h3>
736 <h3>
737 log c @ 7:<a href="/rev/46c1a66bd8fc">46c1a66bd8fc</a>
737 log c @ 7:<a href="/rev/46c1a66bd8fc">46c1a66bd8fc</a>
738 <span class="branchname">a-branch</span> <span class="tag">tip</span>
738 <span class="branchname">a-branch</span> <span class="tag">tip</span>
739 (following lines 1:2 <a href="/log/tip/c">back to filelog</a>)
739 (following lines 1:2 <a href="/log/tip/c">back to filelog</a>)
740 </h3>
740 </h3>
741
741
742 <form class="search" action="/log">
742 <form class="search" action="/log">
743
743
744 <p><input name="rev" id="search1" type="text" size="30" /></p>
744 <p><input name="rev" id="search1" type="text" size="30" /></p>
745 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
745 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
746 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
746 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
747 </form>
747 </form>
748
748
749 <div class="navigate">
749 <div class="navigate">
750 <a href="/log/tip/c?linerange=1%3A2&revcount=30">less</a>
750 <a href="/log/tip/c?linerange=1%3A2&revcount=30">less</a>
751 <a href="/log/tip/c?linerange=1%3A2&revcount=120">more</a>
751 <a href="/log/tip/c?linerange=1%3A2&revcount=120">more</a>
752 | </div>
752 | </div>
753
753
754 <table class="bigtable">
754 <table class="bigtable">
755 <thead>
755 <thead>
756 <tr>
756 <tr>
757 <th class="age">age</th>
757 <th class="age">age</th>
758 <th class="author">author</th>
758 <th class="author">author</th>
759 <th class="description">description</th>
759 <th class="description">description</th>
760 </tr>
760 </tr>
761 </thead>
761 </thead>
762 <tbody class="stripes2">
762 <tbody class="stripes2">
763 <tr>
763 <tr>
764 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
764 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
765 <td class="author">test</td>
765 <td class="author">test</td>
766 <td class="description">
766 <td class="description">
767 <a href="/rev/46c1a66bd8fc">change c</a>
767 <a href="/rev/46c1a66bd8fc">change c</a>
768 <span class="branchhead">a-branch</span> <span class="tag">tip</span>
768 <span class="branchhead">a-branch</span> <span class="tag">tip</span>
769 </td>
769 </td>
770 </tr>
770 </tr>
771
771
772 <tr>
772 <tr>
773 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
773 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
774 <td class="author">test</td>
774 <td class="author">test</td>
775 <td class="description">
775 <td class="description">
776 <a href="/rev/6563da9dcf87">b</a>
776 <a href="/rev/6563da9dcf87">b</a>
777
777
778 </td>
778 </td>
779 </tr>
779 </tr>
780
780
781
781
782 </tbody>
782 </tbody>
783 </table>
783 </table>
784
784
785 <div class="navigate">
785 <div class="navigate">
786 <a href="/log/tip/c?linerange=1%3A2&revcount=30">less</a>
786 <a href="/log/tip/c?linerange=1%3A2&revcount=30">less</a>
787 <a href="/log/tip/c?linerange=1%3A2&revcount=120">more</a>
787 <a href="/log/tip/c?linerange=1%3A2&revcount=120">more</a>
788 |
788 |
789 </div>
789 </div>
790
790
791 </div>
791 </div>
792 </div>
792 </div>
793
793
794
794
795
795
796 </body>
796 </body>
797 </html>
797 </html>
798
798
799 $ (get-with-headers.py localhost:$HGPORT 'log/tip/c?linerange=1%3A2&revcount=1')
799 $ (get-with-headers.py localhost:$HGPORT 'log/tip/c?linerange=1%3A2&revcount=1')
800 200 Script output follows
800 200 Script output follows
801
801
802 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
802 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
803 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
803 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
804 <head>
804 <head>
805 <link rel="icon" href="/static/hgicon.png" type="image/png" />
805 <link rel="icon" href="/static/hgicon.png" type="image/png" />
806 <meta name="robots" content="index, nofollow" />
806 <meta name="robots" content="index, nofollow" />
807 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
807 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
808 <script type="text/javascript" src="/static/mercurial.js"></script>
808 <script type="text/javascript" src="/static/mercurial.js"></script>
809
809
810 <title>test: c history</title>
810 <title>test: c history</title>
811 <link rel="alternate" type="application/atom+xml"
811 <link rel="alternate" type="application/atom+xml"
812 href="/atom-log/tip/c" title="Atom feed for test:c" />
812 href="/atom-log/tip/c" title="Atom feed for test:c" />
813 <link rel="alternate" type="application/rss+xml"
813 <link rel="alternate" type="application/rss+xml"
814 href="/rss-log/tip/c" title="RSS feed for test:c" />
814 href="/rss-log/tip/c" title="RSS feed for test:c" />
815 </head>
815 </head>
816 <body>
816 <body>
817
817
818 <div class="container">
818 <div class="container">
819 <div class="menu">
819 <div class="menu">
820 <div class="logo">
820 <div class="logo">
821 <a href="https://mercurial-scm.org/">
821 <a href="https://mercurial-scm.org/">
822 <img src="/static/hglogo.png" alt="mercurial" /></a>
822 <img src="/static/hglogo.png" alt="mercurial" /></a>
823 </div>
823 </div>
824 <ul>
824 <ul>
825 <li><a href="/shortlog/tip?revcount=1">log</a></li>
825 <li><a href="/shortlog/tip?revcount=1">log</a></li>
826 <li><a href="/graph/tip?revcount=1">graph</a></li>
826 <li><a href="/graph/tip?revcount=1">graph</a></li>
827 <li><a href="/tags?revcount=1">tags</a></li>
827 <li><a href="/tags?revcount=1">tags</a></li>
828 <li><a href="/bookmarks?revcount=1">bookmarks</a></li>
828 <li><a href="/bookmarks?revcount=1">bookmarks</a></li>
829 <li><a href="/branches?revcount=1">branches</a></li>
829 <li><a href="/branches?revcount=1">branches</a></li>
830 </ul>
830 </ul>
831 <ul>
831 <ul>
832 <li><a href="/rev/tip?revcount=1">changeset</a></li>
832 <li><a href="/rev/tip?revcount=1">changeset</a></li>
833 <li><a href="/file/tip?revcount=1">browse</a></li>
833 <li><a href="/file/tip?revcount=1">browse</a></li>
834 </ul>
834 </ul>
835 <ul>
835 <ul>
836 <li><a href="/file/tip/c?revcount=1">file</a></li>
836 <li><a href="/file/tip/c?revcount=1">file</a></li>
837 <li><a href="/diff/tip/c?revcount=1">diff</a></li>
837 <li><a href="/diff/tip/c?revcount=1">diff</a></li>
838 <li><a href="/comparison/tip/c?revcount=1">comparison</a></li>
838 <li><a href="/comparison/tip/c?revcount=1">comparison</a></li>
839 <li><a href="/annotate/tip/c?revcount=1">annotate</a></li>
839 <li><a href="/annotate/tip/c?revcount=1">annotate</a></li>
840 <li class="active">file log</li>
840 <li class="active">file log</li>
841 <li><a href="/raw-file/tip/c">raw</a></li>
841 <li><a href="/raw-file/tip/c">raw</a></li>
842 </ul>
842 </ul>
843 <ul>
843 <ul>
844 <li><a href="/help?revcount=1">help</a></li>
844 <li><a href="/help?revcount=1">help</a></li>
845 </ul>
845 </ul>
846 <div class="atom-logo">
846 <div class="atom-logo">
847 <a href="/atom-log/tip/c" title="subscribe to atom feed">
847 <a href="/atom-log/tip/c" title="subscribe to atom feed">
848 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
848 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
849 </a>
849 </a>
850 </div>
850 </div>
851 </div>
851 </div>
852
852
853 <div class="main">
853 <div class="main">
854 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
854 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
855 <h3>
855 <h3>
856 log c @ 7:<a href="/rev/46c1a66bd8fc?revcount=1">46c1a66bd8fc</a>
856 log c @ 7:<a href="/rev/46c1a66bd8fc?revcount=1">46c1a66bd8fc</a>
857 <span class="branchname">a-branch</span> <span class="tag">tip</span>
857 <span class="branchname">a-branch</span> <span class="tag">tip</span>
858 (following lines 1:2 <a href="/log/tip/c?revcount=1">back to filelog</a>)
858 (following lines 1:2 <a href="/log/tip/c?revcount=1">back to filelog</a>)
859 </h3>
859 </h3>
860
860
861 <form class="search" action="/log">
861 <form class="search" action="/log">
862 <input type="hidden" name="revcount" value="1" />
862 <input type="hidden" name="revcount" value="1" />
863 <p><input name="rev" id="search1" type="text" size="30" /></p>
863 <p><input name="rev" id="search1" type="text" size="30" /></p>
864 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
864 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
865 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
865 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
866 </form>
866 </form>
867
867
868 <div class="navigate">
868 <div class="navigate">
869 <a href="/log/tip/c?linerange=1%3A2&revcount=1">less</a>
869 <a href="/log/tip/c?linerange=1%3A2&revcount=1">less</a>
870 <a href="/log/tip/c?linerange=1%3A2&revcount=2">more</a>
870 <a href="/log/tip/c?linerange=1%3A2&revcount=2">more</a>
871 | </div>
871 | </div>
872
872
873 <table class="bigtable">
873 <table class="bigtable">
874 <thead>
874 <thead>
875 <tr>
875 <tr>
876 <th class="age">age</th>
876 <th class="age">age</th>
877 <th class="author">author</th>
877 <th class="author">author</th>
878 <th class="description">description</th>
878 <th class="description">description</th>
879 </tr>
879 </tr>
880 </thead>
880 </thead>
881 <tbody class="stripes2">
881 <tbody class="stripes2">
882 <tr>
882 <tr>
883 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
883 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
884 <td class="author">test</td>
884 <td class="author">test</td>
885 <td class="description">
885 <td class="description">
886 <a href="/rev/46c1a66bd8fc?revcount=1">change c</a>
886 <a href="/rev/46c1a66bd8fc?revcount=1">change c</a>
887 <span class="branchhead">a-branch</span> <span class="tag">tip</span>
887 <span class="branchhead">a-branch</span> <span class="tag">tip</span>
888 </td>
888 </td>
889 </tr>
889 </tr>
890
890
891
891
892 </tbody>
892 </tbody>
893 </table>
893 </table>
894
894
895 <div class="navigate">
895 <div class="navigate">
896 <a href="/log/tip/c?linerange=1%3A2&revcount=1">less</a>
896 <a href="/log/tip/c?linerange=1%3A2&revcount=1">less</a>
897 <a href="/log/tip/c?linerange=1%3A2&revcount=2">more</a>
897 <a href="/log/tip/c?linerange=1%3A2&revcount=2">more</a>
898 |
898 |
899 </div>
899 </div>
900
900
901 </div>
901 </div>
902 </div>
902 </div>
903
903
904
904
905
905
906 </body>
906 </body>
907 </html>
907 </html>
908
908
909 $ (get-with-headers.py localhost:$HGPORT 'log/3/a?linerange=1' --headeronly)
909 $ (get-with-headers.py localhost:$HGPORT 'log/3/a?linerange=1' --headeronly)
910 400 invalid linerange parameter
910 400 invalid linerange parameter
911 [1]
911 [1]
912 $ (get-with-headers.py localhost:$HGPORT 'log/3/a?linerange=1:a' --headeronly)
912 $ (get-with-headers.py localhost:$HGPORT 'log/3/a?linerange=1:a' --headeronly)
913 400 invalid linerange parameter
913 400 invalid linerange parameter
914 [1]
914 [1]
915 $ (get-with-headers.py localhost:$HGPORT 'log/3/a?linerange=1:2&linerange=3:4' --headeronly)
915 $ (get-with-headers.py localhost:$HGPORT 'log/3/a?linerange=1:2&linerange=3:4' --headeronly)
916 400 redundant linerange parameter
916 400 redundant linerange parameter
917 [1]
917 [1]
918 $ (get-with-headers.py localhost:$HGPORT 'log/3/a?linerange=3:2' --headeronly)
918 $ (get-with-headers.py localhost:$HGPORT 'log/3/a?linerange=3:2' --headeronly)
919 400 line range must be positive
919 400 line range must be positive
920 [1]
920 [1]
921 $ (get-with-headers.py localhost:$HGPORT 'log/3/a?linerange=0:1' --headeronly)
921 $ (get-with-headers.py localhost:$HGPORT 'log/3/a?linerange=0:1' --headeronly)
922 400 fromline must be strictly positive
922 400 fromline must be strictly positive
923 [1]
923 [1]
924
924
925 should show base link, use spartan because it shows it
925 should show base link, use spartan because it shows it
926
926
927 $ (get-with-headers.py localhost:$HGPORT 'log/tip/c?style=spartan')
927 $ (get-with-headers.py localhost:$HGPORT 'log/tip/c?style=spartan')
928 200 Script output follows
928 200 Script output follows
929
929
930 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
930 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
931 <html>
931 <html>
932 <head>
932 <head>
933 <link rel="icon" href="/static/hgicon.png" type="image/png">
933 <link rel="icon" href="/static/hgicon.png" type="image/png">
934 <meta name="robots" content="index, nofollow" />
934 <meta name="robots" content="index, nofollow" />
935 <link rel="stylesheet" href="/static/style.css" type="text/css" />
935 <link rel="stylesheet" href="/static/style.css" type="text/css" />
936 <script type="text/javascript" src="/static/mercurial.js"></script>
936 <script type="text/javascript" src="/static/mercurial.js"></script>
937
937
938 <title>test: c history</title>
938 <title>test: c history</title>
939 <link rel="alternate" type="application/atom+xml"
939 <link rel="alternate" type="application/atom+xml"
940 href="/atom-log/tip/c" title="Atom feed for test:c">
940 href="/atom-log/tip/c" title="Atom feed for test:c">
941 <link rel="alternate" type="application/rss+xml"
941 <link rel="alternate" type="application/rss+xml"
942 href="/rss-log/tip/c" title="RSS feed for test:c">
942 href="/rss-log/tip/c" title="RSS feed for test:c">
943 </head>
943 </head>
944 <body>
944 <body>
945
945
946 <div class="buttons">
946 <div class="buttons">
947 <a href="/log?style=spartan">changelog</a>
947 <a href="/log?style=spartan">changelog</a>
948 <a href="/shortlog?style=spartan">shortlog</a>
948 <a href="/shortlog?style=spartan">shortlog</a>
949 <a href="/graph?style=spartan">graph</a>
949 <a href="/graph?style=spartan">graph</a>
950 <a href="/tags?style=spartan">tags</a>
950 <a href="/tags?style=spartan">tags</a>
951 <a href="/branches?style=spartan">branches</a>
951 <a href="/branches?style=spartan">branches</a>
952 <a href="/file/tip/c?style=spartan">file</a>
952 <a href="/file/tip/c?style=spartan">file</a>
953 <a href="/annotate/tip/c?style=spartan">annotate</a>
953 <a href="/annotate/tip/c?style=spartan">annotate</a>
954 <a href="/help?style=spartan">help</a>
954 <a href="/help?style=spartan">help</a>
955 <a type="application/rss+xml" href="/rss-log/tip/c">rss</a>
955 <a type="application/rss+xml" href="/rss-log/tip/c">rss</a>
956 <a type="application/atom+xml" href="/atom-log/tip/c" title="Atom feed for test:c">atom</a>
956 <a type="application/atom+xml" href="/atom-log/tip/c" title="Atom feed for test:c">atom</a>
957 </div>
957 </div>
958
958
959 <h2><a href="/">Mercurial</a> / c revision history</h2>
959 <h2><a href="/">Mercurial</a> / c revision history</h2>
960
960
961 <p>navigate: <small class="navigate"><a href="/log/c9637d3cc8ef/c?style=spartan">(0)</a> <a href="/log/tip/c?style=spartan">tip</a> </small></p>
961 <p>navigate: <small class="navigate"><a href="/log/c9637d3cc8ef/c?style=spartan">(0)</a> <a href="/log/tip/c?style=spartan">tip</a> </small></p>
962
962
963 <table class="logEntry parity0">
963 <table class="logEntry parity0">
964 <tr>
964 <tr>
965 <th class="label"><span class="age">Thu, 01 Jan 1970 00:00:00 +0000</span>:</th>
965 <th class="label"><span class="age">Thu, 01 Jan 1970 00:00:00 +0000</span>:</th>
966 <th class="firstline"><a href="/rev/46c1a66bd8fc?style=spartan">change c</a></th>
966 <th class="firstline"><a href="/rev/46c1a66bd8fc?style=spartan">change c</a></th>
967 </tr>
967 </tr>
968 <tr>
968 <tr>
969 <th class="revision">revision 1:</th>
969 <th class="revision">revision 1:</th>
970 <td class="node">
970 <td class="node">
971 <a href="/file/46c1a66bd8fc/c?style=spartan">46c1a66bd8fc</a>
971 <a href="/file/46c1a66bd8fc/c?style=spartan">46c1a66bd8fc</a>
972 <a href="/diff/46c1a66bd8fc/c?style=spartan">(diff)</a>
972 <a href="/diff/46c1a66bd8fc/c?style=spartan">(diff)</a>
973 <a href="/annotate/46c1a66bd8fc/c?style=spartan">(annotate)</a>
973 <a href="/annotate/46c1a66bd8fc/c?style=spartan">(annotate)</a>
974 </td>
974 </td>
975 </tr>
975 </tr>
976
976
977 <tr>
977 <tr>
978 <th class="author">author:</th>
978 <th class="author">author:</th>
979 <td class="author">&#116;&#101;&#115;&#116;</td>
979 <td class="author">&#116;&#101;&#115;&#116;</td>
980 </tr>
980 </tr>
981 <tr>
981 <tr>
982 <th class="date">date:</th>
982 <th class="date">date:</th>
983 <td class="date">Thu, 01 Jan 1970 00:00:00 +0000</td>
983 <td class="date">Thu, 01 Jan 1970 00:00:00 +0000</td>
984 </tr>
984 </tr>
985 </table>
985 </table>
986
986
987
987
988 <table class="logEntry parity1">
988 <table class="logEntry parity1">
989 <tr>
989 <tr>
990 <th class="label"><span class="age">Thu, 01 Jan 1970 00:00:00 +0000</span>:</th>
990 <th class="label"><span class="age">Thu, 01 Jan 1970 00:00:00 +0000</span>:</th>
991 <th class="firstline"><a href="/rev/c9637d3cc8ef?style=spartan">mv b</a></th>
991 <th class="firstline"><a href="/rev/c9637d3cc8ef?style=spartan">mv b</a></th>
992 </tr>
992 </tr>
993 <tr>
993 <tr>
994 <th class="revision">revision 0:</th>
994 <th class="revision">revision 0:</th>
995 <td class="node">
995 <td class="node">
996 <a href="/file/c9637d3cc8ef/c?style=spartan">c9637d3cc8ef</a>
996 <a href="/file/c9637d3cc8ef/c?style=spartan">c9637d3cc8ef</a>
997 <a href="/diff/c9637d3cc8ef/c?style=spartan">(diff)</a>
997 <a href="/diff/c9637d3cc8ef/c?style=spartan">(diff)</a>
998 <a href="/annotate/c9637d3cc8ef/c?style=spartan">(annotate)</a>
998 <a href="/annotate/c9637d3cc8ef/c?style=spartan">(annotate)</a>
999 </td>
999 </td>
1000 </tr>
1000 </tr>
1001
1001
1002 <tr>
1002 <tr>
1003 <th>base:</th>
1003 <th>base:</th>
1004 <td>
1004 <td>
1005 <a href="/file/1e88685f5dde/b?style=spartan">
1005 <a href="/file/1e88685f5dde/b?style=spartan">
1006 b@1e88685f5dde
1006 b@1e88685f5dde
1007 </a>
1007 </a>
1008 </td>
1008 </td>
1009 </tr>
1009 </tr>
1010 <tr>
1010 <tr>
1011 <th class="author">author:</th>
1011 <th class="author">author:</th>
1012 <td class="author">&#116;&#101;&#115;&#116;</td>
1012 <td class="author">&#116;&#101;&#115;&#116;</td>
1013 </tr>
1013 </tr>
1014 <tr>
1014 <tr>
1015 <th class="date">date:</th>
1015 <th class="date">date:</th>
1016 <td class="date">Thu, 01 Jan 1970 00:00:00 +0000</td>
1016 <td class="date">Thu, 01 Jan 1970 00:00:00 +0000</td>
1017 </tr>
1017 </tr>
1018 </table>
1018 </table>
1019
1019
1020
1020
1021
1021
1022
1022
1023
1023
1024 <div class="logo">
1024 <div class="logo">
1025 <a href="https://mercurial-scm.org/">
1025 <a href="https://mercurial-scm.org/">
1026 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
1026 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
1027 </div>
1027 </div>
1028
1028
1029 </body>
1029 </body>
1030 </html>
1030 </html>
1031
1031
1032
1032
1033 filelog with patch
1033 filelog with patch
1034
1034
1035 $ (get-with-headers.py localhost:$HGPORT 'log/4/a?patch=1')
1035 $ (get-with-headers.py localhost:$HGPORT 'log/4/a?patch=1')
1036 200 Script output follows
1036 200 Script output follows
1037
1037
1038 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1038 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1039 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
1039 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
1040 <head>
1040 <head>
1041 <link rel="icon" href="/static/hgicon.png" type="image/png" />
1041 <link rel="icon" href="/static/hgicon.png" type="image/png" />
1042 <meta name="robots" content="index, nofollow" />
1042 <meta name="robots" content="index, nofollow" />
1043 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
1043 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
1044 <script type="text/javascript" src="/static/mercurial.js"></script>
1044 <script type="text/javascript" src="/static/mercurial.js"></script>
1045
1045
1046 <title>test: a history</title>
1046 <title>test: a history</title>
1047 <link rel="alternate" type="application/atom+xml"
1047 <link rel="alternate" type="application/atom+xml"
1048 href="/atom-log/tip/a" title="Atom feed for test:a" />
1048 href="/atom-log/tip/a" title="Atom feed for test:a" />
1049 <link rel="alternate" type="application/rss+xml"
1049 <link rel="alternate" type="application/rss+xml"
1050 href="/rss-log/tip/a" title="RSS feed for test:a" />
1050 href="/rss-log/tip/a" title="RSS feed for test:a" />
1051 </head>
1051 </head>
1052 <body>
1052 <body>
1053
1053
1054 <div class="container">
1054 <div class="container">
1055 <div class="menu">
1055 <div class="menu">
1056 <div class="logo">
1056 <div class="logo">
1057 <a href="https://mercurial-scm.org/">
1057 <a href="https://mercurial-scm.org/">
1058 <img src="/static/hglogo.png" alt="mercurial" /></a>
1058 <img src="/static/hglogo.png" alt="mercurial" /></a>
1059 </div>
1059 </div>
1060 <ul>
1060 <ul>
1061 <li><a href="/shortlog/4">log</a></li>
1061 <li><a href="/shortlog/4">log</a></li>
1062 <li><a href="/graph/4">graph</a></li>
1062 <li><a href="/graph/4">graph</a></li>
1063 <li><a href="/tags">tags</a></li>
1063 <li><a href="/tags">tags</a></li>
1064 <li><a href="/bookmarks">bookmarks</a></li>
1064 <li><a href="/bookmarks">bookmarks</a></li>
1065 <li><a href="/branches">branches</a></li>
1065 <li><a href="/branches">branches</a></li>
1066 </ul>
1066 </ul>
1067 <ul>
1067 <ul>
1068 <li><a href="/rev/4">changeset</a></li>
1068 <li><a href="/rev/4">changeset</a></li>
1069 <li><a href="/file/4">browse</a></li>
1069 <li><a href="/file/4">browse</a></li>
1070 </ul>
1070 </ul>
1071 <ul>
1071 <ul>
1072 <li><a href="/file/4/a">file</a></li>
1072 <li><a href="/file/4/a">file</a></li>
1073 <li><a href="/diff/4/a">diff</a></li>
1073 <li><a href="/diff/4/a">diff</a></li>
1074 <li><a href="/comparison/4/a">comparison</a></li>
1074 <li><a href="/comparison/4/a">comparison</a></li>
1075 <li><a href="/annotate/4/a">annotate</a></li>
1075 <li><a href="/annotate/4/a">annotate</a></li>
1076 <li class="active">file log</li>
1076 <li class="active">file log</li>
1077 <li><a href="/raw-file/4/a">raw</a></li>
1077 <li><a href="/raw-file/4/a">raw</a></li>
1078 </ul>
1078 </ul>
1079 <ul>
1079 <ul>
1080 <li><a href="/help">help</a></li>
1080 <li><a href="/help">help</a></li>
1081 </ul>
1081 </ul>
1082 <div class="atom-logo">
1082 <div class="atom-logo">
1083 <a href="/atom-log/tip/a" title="subscribe to atom feed">
1083 <a href="/atom-log/tip/a" title="subscribe to atom feed">
1084 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
1084 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
1085 </a>
1085 </a>
1086 </div>
1086 </div>
1087 </div>
1087 </div>
1088
1088
1089 <div class="main">
1089 <div class="main">
1090 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
1090 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
1091 <h3>
1091 <h3>
1092 log a @ 4:<a href="/rev/3f41bc784e7e">3f41bc784e7e</a>
1092 log a @ 4:<a href="/rev/3f41bc784e7e">3f41bc784e7e</a>
1093 <span class="branchname">a-branch</span>
1093 <span class="branchname">a-branch</span>
1094
1094
1095 </h3>
1095 </h3>
1096
1096
1097 <form class="search" action="/log">
1097 <form class="search" action="/log">
1098
1098
1099 <p><input name="rev" id="search1" type="text" size="30" /></p>
1099 <p><input name="rev" id="search1" type="text" size="30" /></p>
1100 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
1100 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
1101 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
1101 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
1102 </form>
1102 </form>
1103
1103
1104 <div class="navigate">
1104 <div class="navigate">
1105 <a href="/log/4/a?patch=1&revcount=30">less</a>
1105 <a href="/log/4/a?patch=1&revcount=30">less</a>
1106 <a href="/log/4/a?patch=1&revcount=120">more</a>
1106 <a href="/log/4/a?patch=1&revcount=120">more</a>
1107 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
1107 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a> </div>
1108
1108
1109 <table class="bigtable">
1109 <table class="bigtable">
1110 <thead>
1110 <thead>
1111 <tr>
1111 <tr>
1112 <th class="age">age</th>
1112 <th class="age">age</th>
1113 <th class="author">author</th>
1113 <th class="author">author</th>
1114 <th class="description">description</th>
1114 <th class="description">description</th>
1115 </tr>
1115 </tr>
1116 </thead>
1116 </thead>
1117 <tbody class="stripes2">
1117 <tbody class="stripes2">
1118 <tr>
1118 <tr>
1119 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1119 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1120 <td class="author">test</td>
1120 <td class="author">test</td>
1121 <td class="description">
1121 <td class="description">
1122 <a href="/rev/3f41bc784e7e">second a</a>
1122 <a href="/rev/3f41bc784e7e">second a</a>
1123 <span class="branchname">a-branch</span>
1123 <span class="branchname">a-branch</span>
1124 </td>
1124 </td>
1125 </tr>
1125 </tr>
1126 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1126 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1127 <span id="l1.1" class="minusline">--- /dev/null Thu Jan 01 00:00:00 1970 +0000</span><a href="#l1.1"></a>
1127 <span id="3f41bc784e7e-l1.1" class="minusline">--- /dev/null Thu Jan 01 00:00:00 1970 +0000</span><a href="#3f41bc784e7e-l1.1"></a>
1128 <span id="l1.2" class="plusline">+++ b/a Thu Jan 01 00:00:00 1970 +0000</span><a href="#l1.2"></a>
1128 <span id="3f41bc784e7e-l1.2" class="plusline">+++ b/a Thu Jan 01 00:00:00 1970 +0000</span><a href="#3f41bc784e7e-l1.2"></a>
1129 <span id="l1.3" class="atline">@@ -0,0 +1,1 @@</span><a href="#l1.3"></a>
1129 <span id="3f41bc784e7e-l1.3" class="atline">@@ -0,0 +1,1 @@</span><a href="#3f41bc784e7e-l1.3"></a>
1130 <span id="l1.4" class="plusline">+b</span><a href="#l1.4"></a></pre></div></td></tr>
1130 <span id="3f41bc784e7e-l1.4" class="plusline">+b</span><a href="#3f41bc784e7e-l1.4"></a></pre></div></td></tr>
1131 <tr>
1131 <tr>
1132 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1132 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1133 <td class="author">test</td>
1133 <td class="author">test</td>
1134 <td class="description">
1134 <td class="description">
1135 <a href="/rev/5ed941583260">first a</a>
1135 <a href="/rev/5ed941583260">first a</a>
1136 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
1136 <span class="tag">a-tag</span> <span class="tag">a-bookmark</span>
1137 </td>
1137 </td>
1138 </tr>
1138 </tr>
1139 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1139 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1140 <span id="l1.1" class="minusline">--- /dev/null Thu Jan 01 00:00:00 1970 +0000</span><a href="#l1.1"></a>
1140 <span id="5ed941583260-l1.1" class="minusline">--- /dev/null Thu Jan 01 00:00:00 1970 +0000</span><a href="#5ed941583260-l1.1"></a>
1141 <span id="l1.2" class="plusline">+++ b/a Thu Jan 01 00:00:00 1970 +0000</span><a href="#l1.2"></a>
1141 <span id="5ed941583260-l1.2" class="plusline">+++ b/a Thu Jan 01 00:00:00 1970 +0000</span><a href="#5ed941583260-l1.2"></a>
1142 <span id="l1.3" class="atline">@@ -0,0 +1,1 @@</span><a href="#l1.3"></a>
1142 <span id="5ed941583260-l1.3" class="atline">@@ -0,0 +1,1 @@</span><a href="#5ed941583260-l1.3"></a>
1143 <span id="l1.4" class="plusline">+a</span><a href="#l1.4"></a></pre></div></td></tr>
1143 <span id="5ed941583260-l1.4" class="plusline">+a</span><a href="#5ed941583260-l1.4"></a></pre></div></td></tr>
1144
1144
1145 </tbody>
1145 </tbody>
1146 </table>
1146 </table>
1147
1147
1148 <div class="navigate">
1148 <div class="navigate">
1149 <a href="/log/4/a?patch=1&revcount=30">less</a>
1149 <a href="/log/4/a?patch=1&revcount=30">less</a>
1150 <a href="/log/4/a?patch=1&revcount=120">more</a>
1150 <a href="/log/4/a?patch=1&revcount=120">more</a>
1151 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a>
1151 | <a href="/log/5ed941583260/a">(0)</a> <a href="/log/tip/a">tip</a>
1152 </div>
1152 </div>
1153
1153
1154 </div>
1154 </div>
1155 </div>
1155 </div>
1156
1156
1157
1157
1158
1158
1159 </body>
1159 </body>
1160 </html>
1160 </html>
1161
1161
1162 filelog with 'linerange' and 'patch'
1162 filelog with 'linerange' and 'patch'
1163
1163
1164 $ cat c
1164 $ cat c
1165 b
1165 b
1166 c
1166 c
1167 $ cat <<EOF > c
1167 $ cat <<EOF > c
1168 > 0
1168 > 0
1169 > 0
1169 > 0
1170 > b
1170 > b
1171 > c+
1171 > c+
1172 >
1172 >
1173 > a
1173 > a
1174 > a
1174 > a
1175 >
1175 >
1176 > d
1176 > d
1177 > e
1177 > e
1178 > f
1178 > f
1179 > EOF
1179 > EOF
1180 $ hg ci -m 'make c bigger and touch its beginning' c
1180 $ hg ci -m 'make c bigger and touch its beginning' c
1181 $ cat <<EOF > c
1181 $ cat <<EOF > c
1182 > 0
1182 > 0
1183 > 0
1183 > 0
1184 > b
1184 > b
1185 > c+
1185 > c+
1186 >
1186 >
1187 > a
1187 > a
1188 > a
1188 > a
1189 >
1189 >
1190 > d
1190 > d
1191 > e+
1191 > e+
1192 > f
1192 > f
1193 > EOF
1193 > EOF
1194 $ hg ci -m 'just touch end of c' c
1194 $ hg ci -m 'just touch end of c' c
1195 $ cat <<EOF > c
1195 $ cat <<EOF > c
1196 > 0
1196 > 0
1197 > 0
1197 > 0
1198 > b
1198 > b
1199 > c++
1199 > c++
1200 >
1200 >
1201 > a
1201 > a
1202 > a
1202 > a
1203 >
1203 >
1204 > d
1204 > d
1205 > e+
1205 > e+
1206 > f
1206 > f
1207 > EOF
1207 > EOF
1208 $ hg ci -m 'touch beginning of c' c
1208 $ hg ci -m 'touch beginning of c' c
1209 $ cat <<EOF > c
1209 $ cat <<EOF > c
1210 > 0
1210 > 0
1211 > 0
1211 > 0
1212 > b-
1212 > b-
1213 > c++
1213 > c++
1214 >
1214 >
1215 > a
1215 > a
1216 > a
1216 > a
1217 >
1217 >
1218 > d
1218 > d
1219 > e+
1219 > e+
1220 > f+
1220 > f+
1221 > EOF
1221 > EOF
1222 $ hg ci -m 'touching beginning and end of c' c
1222 $ hg ci -m 'touching beginning and end of c' c
1223 $ hg log -r 'followlines(c, 3:4, startrev=tip) and follow(c)' -p
1223 $ hg log -r 'followlines(c, 3:4, startrev=tip) and follow(c)' -p
1224 changeset: 0:6563da9dcf87
1224 changeset: 0:6563da9dcf87
1225 user: test
1225 user: test
1226 date: Thu Jan 01 00:00:00 1970 +0000
1226 date: Thu Jan 01 00:00:00 1970 +0000
1227 summary: b
1227 summary: b
1228
1228
1229 diff -r 000000000000 -r 6563da9dcf87 b
1229 diff -r 000000000000 -r 6563da9dcf87 b
1230 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1230 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1231 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1231 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1232 @@ -0,0 +1,1 @@
1232 @@ -0,0 +1,1 @@
1233 +b
1233 +b
1234
1234
1235 changeset: 7:46c1a66bd8fc
1235 changeset: 7:46c1a66bd8fc
1236 branch: a-branch
1236 branch: a-branch
1237 user: test
1237 user: test
1238 date: Thu Jan 01 00:00:00 1970 +0000
1238 date: Thu Jan 01 00:00:00 1970 +0000
1239 summary: change c
1239 summary: change c
1240
1240
1241 diff -r c9637d3cc8ef -r 46c1a66bd8fc c
1241 diff -r c9637d3cc8ef -r 46c1a66bd8fc c
1242 --- a/c Thu Jan 01 00:00:00 1970 +0000
1242 --- a/c Thu Jan 01 00:00:00 1970 +0000
1243 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1243 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1244 @@ -1,1 +1,2 @@
1244 @@ -1,1 +1,2 @@
1245 b
1245 b
1246 +c
1246 +c
1247
1247
1248 changeset: 8:5c6574614c37
1248 changeset: 8:5c6574614c37
1249 branch: a-branch
1249 branch: a-branch
1250 user: test
1250 user: test
1251 date: Thu Jan 01 00:00:00 1970 +0000
1251 date: Thu Jan 01 00:00:00 1970 +0000
1252 summary: make c bigger and touch its beginning
1252 summary: make c bigger and touch its beginning
1253
1253
1254 diff -r 46c1a66bd8fc -r 5c6574614c37 c
1254 diff -r 46c1a66bd8fc -r 5c6574614c37 c
1255 --- a/c Thu Jan 01 00:00:00 1970 +0000
1255 --- a/c Thu Jan 01 00:00:00 1970 +0000
1256 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1256 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1257 @@ -1,2 +1,11 @@
1257 @@ -1,2 +1,11 @@
1258 +0
1258 +0
1259 +0
1259 +0
1260 b
1260 b
1261 -c
1261 -c
1262 +c+
1262 +c+
1263 +
1263 +
1264 +a
1264 +a
1265 +a
1265 +a
1266 +
1266 +
1267 +d
1267 +d
1268 +e
1268 +e
1269 +f
1269 +f
1270
1270
1271 changeset: 10:e95928d60479
1271 changeset: 10:e95928d60479
1272 branch: a-branch
1272 branch: a-branch
1273 user: test
1273 user: test
1274 date: Thu Jan 01 00:00:00 1970 +0000
1274 date: Thu Jan 01 00:00:00 1970 +0000
1275 summary: touch beginning of c
1275 summary: touch beginning of c
1276
1276
1277 diff -r e1d3e9c5a23f -r e95928d60479 c
1277 diff -r e1d3e9c5a23f -r e95928d60479 c
1278 --- a/c Thu Jan 01 00:00:00 1970 +0000
1278 --- a/c Thu Jan 01 00:00:00 1970 +0000
1279 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1279 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1280 @@ -1,7 +1,7 @@
1280 @@ -1,7 +1,7 @@
1281 0
1281 0
1282 0
1282 0
1283 b
1283 b
1284 -c+
1284 -c+
1285 +c++
1285 +c++
1286
1286
1287 a
1287 a
1288 a
1288 a
1289
1289
1290 changeset: 11:fb9bc322513a
1290 changeset: 11:fb9bc322513a
1291 branch: a-branch
1291 branch: a-branch
1292 tag: tip
1292 tag: tip
1293 user: test
1293 user: test
1294 date: Thu Jan 01 00:00:00 1970 +0000
1294 date: Thu Jan 01 00:00:00 1970 +0000
1295 summary: touching beginning and end of c
1295 summary: touching beginning and end of c
1296
1296
1297 diff -r e95928d60479 -r fb9bc322513a c
1297 diff -r e95928d60479 -r fb9bc322513a c
1298 --- a/c Thu Jan 01 00:00:00 1970 +0000
1298 --- a/c Thu Jan 01 00:00:00 1970 +0000
1299 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1299 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1300 @@ -1,6 +1,6 @@
1300 @@ -1,6 +1,6 @@
1301 0
1301 0
1302 0
1302 0
1303 -b
1303 -b
1304 +b-
1304 +b-
1305 c++
1305 c++
1306
1306
1307 a
1307 a
1308 @@ -8,4 +8,4 @@
1308 @@ -8,4 +8,4 @@
1309
1309
1310 d
1310 d
1311 e+
1311 e+
1312 -f
1312 -f
1313 +f+
1313 +f+
1314
1314
1315 $ (get-with-headers.py localhost:$HGPORT 'log/tip/c?linerange=3:4&patch=')
1315 $ (get-with-headers.py localhost:$HGPORT 'log/tip/c?linerange=3:4&patch=')
1316 200 Script output follows
1316 200 Script output follows
1317
1317
1318 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1318 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1319 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
1319 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
1320 <head>
1320 <head>
1321 <link rel="icon" href="/static/hgicon.png" type="image/png" />
1321 <link rel="icon" href="/static/hgicon.png" type="image/png" />
1322 <meta name="robots" content="index, nofollow" />
1322 <meta name="robots" content="index, nofollow" />
1323 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
1323 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
1324 <script type="text/javascript" src="/static/mercurial.js"></script>
1324 <script type="text/javascript" src="/static/mercurial.js"></script>
1325
1325
1326 <title>test: c history</title>
1326 <title>test: c history</title>
1327 <link rel="alternate" type="application/atom+xml"
1327 <link rel="alternate" type="application/atom+xml"
1328 href="/atom-log/tip/c" title="Atom feed for test:c" />
1328 href="/atom-log/tip/c" title="Atom feed for test:c" />
1329 <link rel="alternate" type="application/rss+xml"
1329 <link rel="alternate" type="application/rss+xml"
1330 href="/rss-log/tip/c" title="RSS feed for test:c" />
1330 href="/rss-log/tip/c" title="RSS feed for test:c" />
1331 </head>
1331 </head>
1332 <body>
1332 <body>
1333
1333
1334 <div class="container">
1334 <div class="container">
1335 <div class="menu">
1335 <div class="menu">
1336 <div class="logo">
1336 <div class="logo">
1337 <a href="https://mercurial-scm.org/">
1337 <a href="https://mercurial-scm.org/">
1338 <img src="/static/hglogo.png" alt="mercurial" /></a>
1338 <img src="/static/hglogo.png" alt="mercurial" /></a>
1339 </div>
1339 </div>
1340 <ul>
1340 <ul>
1341 <li><a href="/shortlog/tip">log</a></li>
1341 <li><a href="/shortlog/tip">log</a></li>
1342 <li><a href="/graph/tip">graph</a></li>
1342 <li><a href="/graph/tip">graph</a></li>
1343 <li><a href="/tags">tags</a></li>
1343 <li><a href="/tags">tags</a></li>
1344 <li><a href="/bookmarks">bookmarks</a></li>
1344 <li><a href="/bookmarks">bookmarks</a></li>
1345 <li><a href="/branches">branches</a></li>
1345 <li><a href="/branches">branches</a></li>
1346 </ul>
1346 </ul>
1347 <ul>
1347 <ul>
1348 <li><a href="/rev/tip">changeset</a></li>
1348 <li><a href="/rev/tip">changeset</a></li>
1349 <li><a href="/file/tip">browse</a></li>
1349 <li><a href="/file/tip">browse</a></li>
1350 </ul>
1350 </ul>
1351 <ul>
1351 <ul>
1352 <li><a href="/file/tip/c">file</a></li>
1352 <li><a href="/file/tip/c">file</a></li>
1353 <li><a href="/diff/tip/c">diff</a></li>
1353 <li><a href="/diff/tip/c">diff</a></li>
1354 <li><a href="/comparison/tip/c">comparison</a></li>
1354 <li><a href="/comparison/tip/c">comparison</a></li>
1355 <li><a href="/annotate/tip/c">annotate</a></li>
1355 <li><a href="/annotate/tip/c">annotate</a></li>
1356 <li class="active">file log</li>
1356 <li class="active">file log</li>
1357 <li><a href="/raw-file/tip/c">raw</a></li>
1357 <li><a href="/raw-file/tip/c">raw</a></li>
1358 </ul>
1358 </ul>
1359 <ul>
1359 <ul>
1360 <li><a href="/help">help</a></li>
1360 <li><a href="/help">help</a></li>
1361 </ul>
1361 </ul>
1362 <div class="atom-logo">
1362 <div class="atom-logo">
1363 <a href="/atom-log/tip/c" title="subscribe to atom feed">
1363 <a href="/atom-log/tip/c" title="subscribe to atom feed">
1364 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
1364 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="atom feed" />
1365 </a>
1365 </a>
1366 </div>
1366 </div>
1367 </div>
1367 </div>
1368
1368
1369 <div class="main">
1369 <div class="main">
1370 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
1370 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
1371 <h3>
1371 <h3>
1372 log c @ 11:<a href="/rev/fb9bc322513a">fb9bc322513a</a>
1372 log c @ 11:<a href="/rev/fb9bc322513a">fb9bc322513a</a>
1373 <span class="branchname">a-branch</span> <span class="tag">tip</span>
1373 <span class="branchname">a-branch</span> <span class="tag">tip</span>
1374 (following lines 3:4 <a href="/log/tip/c">back to filelog</a>)
1374 (following lines 3:4 <a href="/log/tip/c">back to filelog</a>)
1375 </h3>
1375 </h3>
1376
1376
1377 <form class="search" action="/log">
1377 <form class="search" action="/log">
1378
1378
1379 <p><input name="rev" id="search1" type="text" size="30" /></p>
1379 <p><input name="rev" id="search1" type="text" size="30" /></p>
1380 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
1380 <div id="hint">Find changesets by keywords (author, files, the commit message), revision
1381 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
1381 number or hash, or <a href="/help/revsets">revset expression</a>.</div>
1382 </form>
1382 </form>
1383
1383
1384 <div class="navigate">
1384 <div class="navigate">
1385 <a href="/log/tip/c?linerange=3%3A4&patch=&revcount=30">less</a>
1385 <a href="/log/tip/c?linerange=3%3A4&patch=&revcount=30">less</a>
1386 <a href="/log/tip/c?linerange=3%3A4&patch=&revcount=120">more</a>
1386 <a href="/log/tip/c?linerange=3%3A4&patch=&revcount=120">more</a>
1387 | </div>
1387 | </div>
1388
1388
1389 <table class="bigtable">
1389 <table class="bigtable">
1390 <thead>
1390 <thead>
1391 <tr>
1391 <tr>
1392 <th class="age">age</th>
1392 <th class="age">age</th>
1393 <th class="author">author</th>
1393 <th class="author">author</th>
1394 <th class="description">description</th>
1394 <th class="description">description</th>
1395 </tr>
1395 </tr>
1396 </thead>
1396 </thead>
1397 <tbody class="stripes2">
1397 <tbody class="stripes2">
1398 <tr>
1398 <tr>
1399 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1399 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1400 <td class="author">test</td>
1400 <td class="author">test</td>
1401 <td class="description">
1401 <td class="description">
1402 <a href="/rev/fb9bc322513a">touching beginning and end of c</a>
1402 <a href="/rev/fb9bc322513a">touching beginning and end of c</a>
1403 <span class="branchhead">a-branch</span> <span class="tag">tip</span>
1403 <span class="branchhead">a-branch</span> <span class="tag">tip</span>
1404 </td>
1404 </td>
1405 </tr>
1405 </tr>
1406 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1406 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1407 <span id="l1.1" class="minusline">--- a/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#l1.1"></a>
1407 <span id="fb9bc322513a-l1.1" class="minusline">--- a/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#fb9bc322513a-l1.1"></a>
1408 <span id="l1.2" class="plusline">+++ b/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#l1.2"></a>
1408 <span id="fb9bc322513a-l1.2" class="plusline">+++ b/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#fb9bc322513a-l1.2"></a>
1409 <span id="l1.3" class="atline">@@ -1,6 +1,6 @@</span><a href="#l1.3"></a>
1409 <span id="fb9bc322513a-l1.3" class="atline">@@ -1,6 +1,6 @@</span><a href="#fb9bc322513a-l1.3"></a>
1410 <span id="l1.4"> 0</span><a href="#l1.4"></a>
1410 <span id="fb9bc322513a-l1.4"> 0</span><a href="#fb9bc322513a-l1.4"></a>
1411 <span id="l1.5"> 0</span><a href="#l1.5"></a>
1411 <span id="fb9bc322513a-l1.5"> 0</span><a href="#fb9bc322513a-l1.5"></a>
1412 <span id="l1.6" class="minusline">-b</span><a href="#l1.6"></a>
1412 <span id="fb9bc322513a-l1.6" class="minusline">-b</span><a href="#fb9bc322513a-l1.6"></a>
1413 <span id="l1.7" class="plusline">+b-</span><a href="#l1.7"></a>
1413 <span id="fb9bc322513a-l1.7" class="plusline">+b-</span><a href="#fb9bc322513a-l1.7"></a>
1414 <span id="l1.8"> c++</span><a href="#l1.8"></a>
1414 <span id="fb9bc322513a-l1.8"> c++</span><a href="#fb9bc322513a-l1.8"></a>
1415 <span id="l1.9"> </span><a href="#l1.9"></a>
1415 <span id="fb9bc322513a-l1.9"> </span><a href="#fb9bc322513a-l1.9"></a>
1416 <span id="l1.10"> a</span><a href="#l1.10"></a></pre></div></td></tr>
1416 <span id="fb9bc322513a-l1.10"> a</span><a href="#fb9bc322513a-l1.10"></a></pre></div></td></tr>
1417 <tr>
1417 <tr>
1418 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1418 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1419 <td class="author">test</td>
1419 <td class="author">test</td>
1420 <td class="description">
1420 <td class="description">
1421 <a href="/rev/e95928d60479">touch beginning of c</a>
1421 <a href="/rev/e95928d60479">touch beginning of c</a>
1422 <span class="branchname">a-branch</span>
1422 <span class="branchname">a-branch</span>
1423 </td>
1423 </td>
1424 </tr>
1424 </tr>
1425 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1425 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1426 <span id="l1.1" class="minusline">--- a/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#l1.1"></a>
1426 <span id="e95928d60479-l1.1" class="minusline">--- a/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#e95928d60479-l1.1"></a>
1427 <span id="l1.2" class="plusline">+++ b/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#l1.2"></a>
1427 <span id="e95928d60479-l1.2" class="plusline">+++ b/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#e95928d60479-l1.2"></a>
1428 <span id="l1.3" class="atline">@@ -1,7 +1,7 @@</span><a href="#l1.3"></a>
1428 <span id="e95928d60479-l1.3" class="atline">@@ -1,7 +1,7 @@</span><a href="#e95928d60479-l1.3"></a>
1429 <span id="l1.4"> 0</span><a href="#l1.4"></a>
1429 <span id="e95928d60479-l1.4"> 0</span><a href="#e95928d60479-l1.4"></a>
1430 <span id="l1.5"> 0</span><a href="#l1.5"></a>
1430 <span id="e95928d60479-l1.5"> 0</span><a href="#e95928d60479-l1.5"></a>
1431 <span id="l1.6"> b</span><a href="#l1.6"></a>
1431 <span id="e95928d60479-l1.6"> b</span><a href="#e95928d60479-l1.6"></a>
1432 <span id="l1.7" class="minusline">-c+</span><a href="#l1.7"></a>
1432 <span id="e95928d60479-l1.7" class="minusline">-c+</span><a href="#e95928d60479-l1.7"></a>
1433 <span id="l1.8" class="plusline">+c++</span><a href="#l1.8"></a>
1433 <span id="e95928d60479-l1.8" class="plusline">+c++</span><a href="#e95928d60479-l1.8"></a>
1434 <span id="l1.9"> </span><a href="#l1.9"></a>
1434 <span id="e95928d60479-l1.9"> </span><a href="#e95928d60479-l1.9"></a>
1435 <span id="l1.10"> a</span><a href="#l1.10"></a>
1435 <span id="e95928d60479-l1.10"> a</span><a href="#e95928d60479-l1.10"></a>
1436 <span id="l1.11"> a</span><a href="#l1.11"></a></pre></div></td></tr>
1436 <span id="e95928d60479-l1.11"> a</span><a href="#e95928d60479-l1.11"></a></pre></div></td></tr>
1437 <tr>
1437 <tr>
1438 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1438 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1439 <td class="author">test</td>
1439 <td class="author">test</td>
1440 <td class="description">
1440 <td class="description">
1441 <a href="/rev/5c6574614c37">make c bigger and touch its beginning</a>
1441 <a href="/rev/5c6574614c37">make c bigger and touch its beginning</a>
1442 <span class="branchname">a-branch</span>
1442 <span class="branchname">a-branch</span>
1443 </td>
1443 </td>
1444 </tr>
1444 </tr>
1445 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1445 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1446 <span id="l1.1" class="minusline">--- a/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#l1.1"></a>
1446 <span id="5c6574614c37-l1.1" class="minusline">--- a/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#5c6574614c37-l1.1"></a>
1447 <span id="l1.2" class="plusline">+++ b/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#l1.2"></a>
1447 <span id="5c6574614c37-l1.2" class="plusline">+++ b/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#5c6574614c37-l1.2"></a>
1448 <span id="l1.3" class="atline">@@ -1,2 +1,11 @@</span><a href="#l1.3"></a>
1448 <span id="5c6574614c37-l1.3" class="atline">@@ -1,2 +1,11 @@</span><a href="#5c6574614c37-l1.3"></a>
1449 <span id="l1.4" class="plusline">+0</span><a href="#l1.4"></a>
1449 <span id="5c6574614c37-l1.4" class="plusline">+0</span><a href="#5c6574614c37-l1.4"></a>
1450 <span id="l1.5" class="plusline">+0</span><a href="#l1.5"></a>
1450 <span id="5c6574614c37-l1.5" class="plusline">+0</span><a href="#5c6574614c37-l1.5"></a>
1451 <span id="l1.6"> b</span><a href="#l1.6"></a>
1451 <span id="5c6574614c37-l1.6"> b</span><a href="#5c6574614c37-l1.6"></a>
1452 <span id="l1.7" class="minusline">-c</span><a href="#l1.7"></a>
1452 <span id="5c6574614c37-l1.7" class="minusline">-c</span><a href="#5c6574614c37-l1.7"></a>
1453 <span id="l1.8" class="plusline">+c+</span><a href="#l1.8"></a>
1453 <span id="5c6574614c37-l1.8" class="plusline">+c+</span><a href="#5c6574614c37-l1.8"></a>
1454 <span id="l1.9" class="plusline">+</span><a href="#l1.9"></a>
1454 <span id="5c6574614c37-l1.9" class="plusline">+</span><a href="#5c6574614c37-l1.9"></a>
1455 <span id="l1.10" class="plusline">+a</span><a href="#l1.10"></a>
1455 <span id="5c6574614c37-l1.10" class="plusline">+a</span><a href="#5c6574614c37-l1.10"></a>
1456 <span id="l1.11" class="plusline">+a</span><a href="#l1.11"></a>
1456 <span id="5c6574614c37-l1.11" class="plusline">+a</span><a href="#5c6574614c37-l1.11"></a>
1457 <span id="l1.12" class="plusline">+</span><a href="#l1.12"></a>
1457 <span id="5c6574614c37-l1.12" class="plusline">+</span><a href="#5c6574614c37-l1.12"></a>
1458 <span id="l1.13" class="plusline">+d</span><a href="#l1.13"></a>
1458 <span id="5c6574614c37-l1.13" class="plusline">+d</span><a href="#5c6574614c37-l1.13"></a>
1459 <span id="l1.14" class="plusline">+e</span><a href="#l1.14"></a>
1459 <span id="5c6574614c37-l1.14" class="plusline">+e</span><a href="#5c6574614c37-l1.14"></a>
1460 <span id="l1.15" class="plusline">+f</span><a href="#l1.15"></a></pre></div></td></tr>
1460 <span id="5c6574614c37-l1.15" class="plusline">+f</span><a href="#5c6574614c37-l1.15"></a></pre></div></td></tr>
1461 <tr>
1461 <tr>
1462 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1462 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1463 <td class="author">test</td>
1463 <td class="author">test</td>
1464 <td class="description">
1464 <td class="description">
1465 <a href="/rev/46c1a66bd8fc">change c</a>
1465 <a href="/rev/46c1a66bd8fc">change c</a>
1466 <span class="branchname">a-branch</span>
1466 <span class="branchname">a-branch</span>
1467 </td>
1467 </td>
1468 </tr>
1468 </tr>
1469 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1469 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1470 <span id="l1.1" class="minusline">--- a/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#l1.1"></a>
1470 <span id="46c1a66bd8fc-l1.1" class="minusline">--- a/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#46c1a66bd8fc-l1.1"></a>
1471 <span id="l1.2" class="plusline">+++ b/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#l1.2"></a>
1471 <span id="46c1a66bd8fc-l1.2" class="plusline">+++ b/c Thu Jan 01 00:00:00 1970 +0000</span><a href="#46c1a66bd8fc-l1.2"></a>
1472 <span id="l1.3" class="atline">@@ -1,1 +1,2 @@</span><a href="#l1.3"></a>
1472 <span id="46c1a66bd8fc-l1.3" class="atline">@@ -1,1 +1,2 @@</span><a href="#46c1a66bd8fc-l1.3"></a>
1473 <span id="l1.4"> b</span><a href="#l1.4"></a>
1473 <span id="46c1a66bd8fc-l1.4"> b</span><a href="#46c1a66bd8fc-l1.4"></a>
1474 <span id="l1.5" class="plusline">+c</span><a href="#l1.5"></a></pre></div></td></tr>
1474 <span id="46c1a66bd8fc-l1.5" class="plusline">+c</span><a href="#46c1a66bd8fc-l1.5"></a></pre></div></td></tr>
1475 <tr>
1475 <tr>
1476 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1476 <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>
1477 <td class="author">test</td>
1477 <td class="author">test</td>
1478 <td class="description">
1478 <td class="description">
1479 <a href="/rev/6563da9dcf87">b</a>
1479 <a href="/rev/6563da9dcf87">b</a>
1480
1480
1481 </td>
1481 </td>
1482 </tr>
1482 </tr>
1483 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1483 <tr><td colspan="3"><div class="bottomline inc-lineno"><pre class="sourcelines wrap">
1484 <span id="l1.1" class="minusline">--- /dev/null Thu Jan 01 00:00:00 1970 +0000</span><a href="#l1.1"></a>
1484 <span id="6563da9dcf87-l1.1" class="minusline">--- /dev/null Thu Jan 01 00:00:00 1970 +0000</span><a href="#6563da9dcf87-l1.1"></a>
1485 <span id="l1.2" class="plusline">+++ b/b Thu Jan 01 00:00:00 1970 +0000</span><a href="#l1.2"></a></pre></div></td></tr>
1485 <span id="6563da9dcf87-l1.2" class="plusline">+++ b/b Thu Jan 01 00:00:00 1970 +0000</span><a href="#6563da9dcf87-l1.2"></a></pre></div></td></tr>
1486
1486
1487 </tbody>
1487 </tbody>
1488 </table>
1488 </table>
1489
1489
1490 <div class="navigate">
1490 <div class="navigate">
1491 <a href="/log/tip/c?linerange=3%3A4&patch=&revcount=30">less</a>
1491 <a href="/log/tip/c?linerange=3%3A4&patch=&revcount=30">less</a>
1492 <a href="/log/tip/c?linerange=3%3A4&patch=&revcount=120">more</a>
1492 <a href="/log/tip/c?linerange=3%3A4&patch=&revcount=120">more</a>
1493 |
1493 |
1494 </div>
1494 </div>
1495
1495
1496 </div>
1496 </div>
1497 </div>
1497 </div>
1498
1498
1499
1499
1500
1500
1501 </body>
1501 </body>
1502 </html>
1502 </html>
1503
1503
1504
1504
1505 rss log
1505 rss log
1506
1506
1507 $ (get-with-headers.py localhost:$HGPORT 'rss-log/tip/a')
1507 $ (get-with-headers.py localhost:$HGPORT 'rss-log/tip/a')
1508 200 Script output follows
1508 200 Script output follows
1509
1509
1510 <?xml version="1.0" encoding="ascii"?>
1510 <?xml version="1.0" encoding="ascii"?>
1511 <rss version="2.0">
1511 <rss version="2.0">
1512 <channel>
1512 <channel>
1513 <link>http://*:$HGPORT/</link> (glob)
1513 <link>http://*:$HGPORT/</link> (glob)
1514 <language>en-us</language>
1514 <language>en-us</language>
1515
1515
1516 <title>test: a history</title>
1516 <title>test: a history</title>
1517 <description>a revision history</description>
1517 <description>a revision history</description>
1518 <item>
1518 <item>
1519 <title>second a</title>
1519 <title>second a</title>
1520 <link>http://*:$HGPORT/log/3f41bc784e7e/a</link> (glob)
1520 <link>http://*:$HGPORT/log/3f41bc784e7e/a</link> (glob)
1521 <description><![CDATA[second a]]></description>
1521 <description><![CDATA[second a]]></description>
1522 <author>&#116;&#101;&#115;&#116;</author>
1522 <author>&#116;&#101;&#115;&#116;</author>
1523 <pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate>
1523 <pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate>
1524 </item>
1524 </item>
1525 <item>
1525 <item>
1526 <title>first a</title>
1526 <title>first a</title>
1527 <link>http://*:$HGPORT/log/5ed941583260/a</link> (glob)
1527 <link>http://*:$HGPORT/log/5ed941583260/a</link> (glob)
1528 <description><![CDATA[first a]]></description>
1528 <description><![CDATA[first a]]></description>
1529 <author>&#116;&#101;&#115;&#116;</author>
1529 <author>&#116;&#101;&#115;&#116;</author>
1530 <pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate>
1530 <pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate>
1531 </item>
1531 </item>
1532
1532
1533 </channel>
1533 </channel>
1534 </rss>
1534 </rss>
1535
1535
1536 atom log
1536 atom log
1537
1537
1538 $ (get-with-headers.py localhost:$HGPORT 'atom-log/tip/a')
1538 $ (get-with-headers.py localhost:$HGPORT 'atom-log/tip/a')
1539 200 Script output follows
1539 200 Script output follows
1540
1540
1541 <?xml version="1.0" encoding="ascii"?>
1541 <?xml version="1.0" encoding="ascii"?>
1542 <feed xmlns="http://www.w3.org/2005/Atom">
1542 <feed xmlns="http://www.w3.org/2005/Atom">
1543 <id>http://*:$HGPORT/atom-log/tip/a</id> (glob)
1543 <id>http://*:$HGPORT/atom-log/tip/a</id> (glob)
1544 <link rel="self" href="http://*:$HGPORT/atom-log/tip/a"/> (glob)
1544 <link rel="self" href="http://*:$HGPORT/atom-log/tip/a"/> (glob)
1545 <title>test: a history</title>
1545 <title>test: a history</title>
1546 <updated>1970-01-01T00:00:00+00:00</updated>
1546 <updated>1970-01-01T00:00:00+00:00</updated>
1547
1547
1548 <entry>
1548 <entry>
1549 <title>[a-branch] second a</title>
1549 <title>[a-branch] second a</title>
1550 <id>http://*:$HGPORT/#changeset-3f41bc784e7e73035c6d47112c6cc7efb673adf8</id> (glob)
1550 <id>http://*:$HGPORT/#changeset-3f41bc784e7e73035c6d47112c6cc7efb673adf8</id> (glob)
1551 <link href="http://*:$HGPORT/rev/3f41bc784e7e"/> (glob)
1551 <link href="http://*:$HGPORT/rev/3f41bc784e7e"/> (glob)
1552 <author>
1552 <author>
1553 <name>test</name>
1553 <name>test</name>
1554 <email>&#116;&#101;&#115;&#116;</email>
1554 <email>&#116;&#101;&#115;&#116;</email>
1555 </author>
1555 </author>
1556 <updated>1970-01-01T00:00:00+00:00</updated>
1556 <updated>1970-01-01T00:00:00+00:00</updated>
1557 <published>1970-01-01T00:00:00+00:00</published>
1557 <published>1970-01-01T00:00:00+00:00</published>
1558 <content type="xhtml">
1558 <content type="xhtml">
1559 <table xmlns="http://www.w3.org/1999/xhtml">
1559 <table xmlns="http://www.w3.org/1999/xhtml">
1560 <tr>
1560 <tr>
1561 <th style="text-align:left;">changeset</th>
1561 <th style="text-align:left;">changeset</th>
1562 <td>3f41bc784e7e</td>
1562 <td>3f41bc784e7e</td>
1563 </tr>
1563 </tr>
1564 <tr>
1564 <tr>
1565 <th style="text-align:left;">branch</th>
1565 <th style="text-align:left;">branch</th>
1566 <td>a-branch</td>
1566 <td>a-branch</td>
1567 </tr>
1567 </tr>
1568 <tr>
1568 <tr>
1569 <th style="text-align:left;">bookmark</th>
1569 <th style="text-align:left;">bookmark</th>
1570 <td></td>
1570 <td></td>
1571 </tr>
1571 </tr>
1572 <tr>
1572 <tr>
1573 <th style="text-align:left;">tag</th>
1573 <th style="text-align:left;">tag</th>
1574 <td></td>
1574 <td></td>
1575 </tr>
1575 </tr>
1576 <tr>
1576 <tr>
1577 <th style="text-align:left;">user</th>
1577 <th style="text-align:left;">user</th>
1578 <td>&#116;&#101;&#115;&#116;</td>
1578 <td>&#116;&#101;&#115;&#116;</td>
1579 </tr>
1579 </tr>
1580 <tr>
1580 <tr>
1581 <th style="text-align:left;vertical-align:top;">description</th>
1581 <th style="text-align:left;vertical-align:top;">description</th>
1582 <td>second a</td>
1582 <td>second a</td>
1583 </tr>
1583 </tr>
1584 <tr>
1584 <tr>
1585 <th style="text-align:left;vertical-align:top;">files</th>
1585 <th style="text-align:left;vertical-align:top;">files</th>
1586 <td></td>
1586 <td></td>
1587 </tr>
1587 </tr>
1588 </table>
1588 </table>
1589 </content>
1589 </content>
1590 </entry>
1590 </entry>
1591 <entry>
1591 <entry>
1592 <title>first a</title>
1592 <title>first a</title>
1593 <id>http://*:$HGPORT/#changeset-5ed941583260248620985524192fdc382ef57c36</id> (glob)
1593 <id>http://*:$HGPORT/#changeset-5ed941583260248620985524192fdc382ef57c36</id> (glob)
1594 <link href="http://*:$HGPORT/rev/5ed941583260"/> (glob)
1594 <link href="http://*:$HGPORT/rev/5ed941583260"/> (glob)
1595 <author>
1595 <author>
1596 <name>test</name>
1596 <name>test</name>
1597 <email>&#116;&#101;&#115;&#116;</email>
1597 <email>&#116;&#101;&#115;&#116;</email>
1598 </author>
1598 </author>
1599 <updated>1970-01-01T00:00:00+00:00</updated>
1599 <updated>1970-01-01T00:00:00+00:00</updated>
1600 <published>1970-01-01T00:00:00+00:00</published>
1600 <published>1970-01-01T00:00:00+00:00</published>
1601 <content type="xhtml">
1601 <content type="xhtml">
1602 <table xmlns="http://www.w3.org/1999/xhtml">
1602 <table xmlns="http://www.w3.org/1999/xhtml">
1603 <tr>
1603 <tr>
1604 <th style="text-align:left;">changeset</th>
1604 <th style="text-align:left;">changeset</th>
1605 <td>5ed941583260</td>
1605 <td>5ed941583260</td>
1606 </tr>
1606 </tr>
1607 <tr>
1607 <tr>
1608 <th style="text-align:left;">branch</th>
1608 <th style="text-align:left;">branch</th>
1609 <td></td>
1609 <td></td>
1610 </tr>
1610 </tr>
1611 <tr>
1611 <tr>
1612 <th style="text-align:left;">bookmark</th>
1612 <th style="text-align:left;">bookmark</th>
1613 <td>a-bookmark</td>
1613 <td>a-bookmark</td>
1614 </tr>
1614 </tr>
1615 <tr>
1615 <tr>
1616 <th style="text-align:left;">tag</th>
1616 <th style="text-align:left;">tag</th>
1617 <td>a-tag</td>
1617 <td>a-tag</td>
1618 </tr>
1618 </tr>
1619 <tr>
1619 <tr>
1620 <th style="text-align:left;">user</th>
1620 <th style="text-align:left;">user</th>
1621 <td>&#116;&#101;&#115;&#116;</td>
1621 <td>&#116;&#101;&#115;&#116;</td>
1622 </tr>
1622 </tr>
1623 <tr>
1623 <tr>
1624 <th style="text-align:left;vertical-align:top;">description</th>
1624 <th style="text-align:left;vertical-align:top;">description</th>
1625 <td>first a</td>
1625 <td>first a</td>
1626 </tr>
1626 </tr>
1627 <tr>
1627 <tr>
1628 <th style="text-align:left;vertical-align:top;">files</th>
1628 <th style="text-align:left;vertical-align:top;">files</th>
1629 <td></td>
1629 <td></td>
1630 </tr>
1630 </tr>
1631 </table>
1631 </table>
1632 </content>
1632 </content>
1633 </entry>
1633 </entry>
1634
1634
1635 </feed>
1635 </feed>
1636
1636
1637 errors
1637 errors
1638
1638
1639 $ cat errors.log
1639 $ cat errors.log
1640
1640
1641 $ cd ..
1641 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now