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