##// END OF EJS Templates
hgweb: do not audit URL path as working-directory path...
Yuya Nishihara -
r39507:15e8250a default
parent child Browse files
Show More
@@ -1,812 +1,813 b''
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 diffutil,
29 29 error,
30 30 match,
31 31 mdiff,
32 32 obsutil,
33 33 patch,
34 34 pathutil,
35 35 pycompat,
36 36 scmutil,
37 37 templatefilters,
38 38 templatekw,
39 39 templateutil,
40 40 ui as uimod,
41 41 util,
42 42 )
43 43
44 44 from ..utils import (
45 45 stringutil,
46 46 )
47 47
48 48 archivespecs = util.sortdict((
49 49 ('zip', ('application/zip', 'zip', '.zip', None)),
50 50 ('gz', ('application/x-gzip', 'tgz', '.tar.gz', None)),
51 51 ('bz2', ('application/x-bzip2', 'tbz2', '.tar.bz2', None)),
52 52 ))
53 53
54 54 def archivelist(ui, nodeid, url=None):
55 55 allowed = ui.configlist('web', 'allow-archive', untrusted=True)
56 56 archives = []
57 57
58 58 for typ, spec in archivespecs.iteritems():
59 59 if typ in allowed or ui.configbool('web', 'allow' + typ,
60 60 untrusted=True):
61 61 archives.append({
62 62 'type': typ,
63 63 'extension': spec[2],
64 64 'node': nodeid,
65 65 'url': url,
66 66 })
67 67
68 68 return templateutil.mappinglist(archives)
69 69
70 70 def up(p):
71 71 if p[0:1] != "/":
72 72 p = "/" + p
73 73 if p[-1:] == "/":
74 74 p = p[:-1]
75 75 up = os.path.dirname(p)
76 76 if up == "/":
77 77 return "/"
78 78 return up + "/"
79 79
80 80 def _navseq(step, firststep=None):
81 81 if firststep:
82 82 yield firststep
83 83 if firststep >= 20 and firststep <= 40:
84 84 firststep = 50
85 85 yield firststep
86 86 assert step > 0
87 87 assert firststep > 0
88 88 while step <= firststep:
89 89 step *= 10
90 90 while True:
91 91 yield 1 * step
92 92 yield 3 * step
93 93 step *= 10
94 94
95 95 class revnav(object):
96 96
97 97 def __init__(self, repo):
98 98 """Navigation generation object
99 99
100 100 :repo: repo object we generate nav for
101 101 """
102 102 # used for hex generation
103 103 self._revlog = repo.changelog
104 104
105 105 def __nonzero__(self):
106 106 """return True if any revision to navigate over"""
107 107 return self._first() is not None
108 108
109 109 __bool__ = __nonzero__
110 110
111 111 def _first(self):
112 112 """return the minimum non-filtered changeset or None"""
113 113 try:
114 114 return next(iter(self._revlog))
115 115 except StopIteration:
116 116 return None
117 117
118 118 def hex(self, rev):
119 119 return hex(self._revlog.node(rev))
120 120
121 121 def gen(self, pos, pagelen, limit):
122 122 """computes label and revision id for navigation link
123 123
124 124 :pos: is the revision relative to which we generate navigation.
125 125 :pagelen: the size of each navigation page
126 126 :limit: how far shall we link
127 127
128 128 The return is:
129 129 - a single element mappinglist
130 130 - containing a dictionary with a `before` and `after` key
131 131 - values are dictionaries with `label` and `node` keys
132 132 """
133 133 if not self:
134 134 # empty repo
135 135 return templateutil.mappinglist([
136 136 {'before': templateutil.mappinglist([]),
137 137 'after': templateutil.mappinglist([])},
138 138 ])
139 139
140 140 targets = []
141 141 for f in _navseq(1, pagelen):
142 142 if f > limit:
143 143 break
144 144 targets.append(pos + f)
145 145 targets.append(pos - f)
146 146 targets.sort()
147 147
148 148 first = self._first()
149 149 navbefore = [{'label': '(%i)' % first, 'node': self.hex(first)}]
150 150 navafter = []
151 151 for rev in targets:
152 152 if rev not in self._revlog:
153 153 continue
154 154 if pos < rev < limit:
155 155 navafter.append({'label': '+%d' % abs(rev - pos),
156 156 'node': self.hex(rev)})
157 157 if 0 < rev < pos:
158 158 navbefore.append({'label': '-%d' % abs(rev - pos),
159 159 'node': self.hex(rev)})
160 160
161 161 navafter.append({'label': 'tip', 'node': 'tip'})
162 162
163 163 # TODO: maybe this can be a scalar object supporting tomap()
164 164 return templateutil.mappinglist([
165 165 {'before': templateutil.mappinglist(navbefore),
166 166 'after': templateutil.mappinglist(navafter)},
167 167 ])
168 168
169 169 class filerevnav(revnav):
170 170
171 171 def __init__(self, repo, path):
172 172 """Navigation generation object
173 173
174 174 :repo: repo object we generate nav for
175 175 :path: path of the file we generate nav for
176 176 """
177 177 # used for iteration
178 178 self._changelog = repo.unfiltered().changelog
179 179 # used for hex generation
180 180 self._revlog = repo.file(path)
181 181
182 182 def hex(self, rev):
183 183 return hex(self._changelog.node(self._revlog.linkrev(rev)))
184 184
185 185 # TODO: maybe this can be a wrapper class for changectx/filectx list, which
186 186 # yields {'ctx': ctx}
187 187 def _ctxsgen(context, ctxs):
188 188 for s in ctxs:
189 189 d = {
190 190 'node': s.hex(),
191 191 'rev': s.rev(),
192 192 'user': s.user(),
193 193 'date': s.date(),
194 194 'description': s.description(),
195 195 'branch': s.branch(),
196 196 }
197 197 if util.safehasattr(s, 'path'):
198 198 d['file'] = s.path()
199 199 yield d
200 200
201 201 def _siblings(siblings=None, hiderev=None):
202 202 if siblings is None:
203 203 siblings = []
204 204 siblings = [s for s in siblings if s.node() != nullid]
205 205 if len(siblings) == 1 and siblings[0].rev() == hiderev:
206 206 siblings = []
207 207 return templateutil.mappinggenerator(_ctxsgen, args=(siblings,))
208 208
209 209 def difffeatureopts(req, ui, section):
210 210 diffopts = diffutil.difffeatureopts(ui, untrusted=True,
211 211 section=section, whitespace=True)
212 212
213 213 for k in ('ignorews', 'ignorewsamount', 'ignorewseol', 'ignoreblanklines'):
214 214 v = req.qsparams.get(k)
215 215 if v is not None:
216 216 v = stringutil.parsebool(v)
217 217 setattr(diffopts, k, v if v is not None else True)
218 218
219 219 return diffopts
220 220
221 221 def annotate(req, fctx, ui):
222 222 diffopts = difffeatureopts(req, ui, 'annotate')
223 223 return fctx.annotate(follow=True, diffopts=diffopts)
224 224
225 225 def parents(ctx, hide=None):
226 226 if isinstance(ctx, context.basefilectx):
227 227 introrev = ctx.introrev()
228 228 if ctx.changectx().rev() != introrev:
229 229 return _siblings([ctx.repo()[introrev]], hide)
230 230 return _siblings(ctx.parents(), hide)
231 231
232 232 def children(ctx, hide=None):
233 233 return _siblings(ctx.children(), hide)
234 234
235 235 def renamelink(fctx):
236 236 r = fctx.renamed()
237 237 if r:
238 238 return templateutil.mappinglist([{'file': r[0], 'node': hex(r[1])}])
239 239 return templateutil.mappinglist([])
240 240
241 241 def nodetagsdict(repo, node):
242 242 return templateutil.hybridlist(repo.nodetags(node), name='name')
243 243
244 244 def nodebookmarksdict(repo, node):
245 245 return templateutil.hybridlist(repo.nodebookmarks(node), name='name')
246 246
247 247 def nodebranchdict(repo, ctx):
248 248 branches = []
249 249 branch = ctx.branch()
250 250 # If this is an empty repo, ctx.node() == nullid,
251 251 # ctx.branch() == 'default'.
252 252 try:
253 253 branchnode = repo.branchtip(branch)
254 254 except error.RepoLookupError:
255 255 branchnode = None
256 256 if branchnode == ctx.node():
257 257 branches.append(branch)
258 258 return templateutil.hybridlist(branches, name='name')
259 259
260 260 def nodeinbranch(repo, ctx):
261 261 branches = []
262 262 branch = ctx.branch()
263 263 try:
264 264 branchnode = repo.branchtip(branch)
265 265 except error.RepoLookupError:
266 266 branchnode = None
267 267 if branch != 'default' and branchnode != ctx.node():
268 268 branches.append(branch)
269 269 return templateutil.hybridlist(branches, name='name')
270 270
271 271 def nodebranchnodefault(ctx):
272 272 branches = []
273 273 branch = ctx.branch()
274 274 if branch != 'default':
275 275 branches.append(branch)
276 276 return templateutil.hybridlist(branches, name='name')
277 277
278 278 def _nodenamesgen(context, f, node, name):
279 279 for t in f(node):
280 280 yield {name: t}
281 281
282 282 def showtag(repo, t1, node=nullid):
283 283 args = (repo.nodetags, node, 'tag')
284 284 return templateutil.mappinggenerator(_nodenamesgen, args=args, name=t1)
285 285
286 286 def showbookmark(repo, t1, node=nullid):
287 287 args = (repo.nodebookmarks, node, 'bookmark')
288 288 return templateutil.mappinggenerator(_nodenamesgen, args=args, name=t1)
289 289
290 290 def branchentries(repo, stripecount, limit=0):
291 291 tips = []
292 292 heads = repo.heads()
293 293 parity = paritygen(stripecount)
294 294 sortkey = lambda item: (not item[1], item[0].rev())
295 295
296 296 def entries(context):
297 297 count = 0
298 298 if not tips:
299 299 for tag, hs, tip, closed in repo.branchmap().iterbranches():
300 300 tips.append((repo[tip], closed))
301 301 for ctx, closed in sorted(tips, key=sortkey, reverse=True):
302 302 if limit > 0 and count >= limit:
303 303 return
304 304 count += 1
305 305 if closed:
306 306 status = 'closed'
307 307 elif ctx.node() not in heads:
308 308 status = 'inactive'
309 309 else:
310 310 status = 'open'
311 311 yield {
312 312 'parity': next(parity),
313 313 'branch': ctx.branch(),
314 314 'status': status,
315 315 'node': ctx.hex(),
316 316 'date': ctx.date()
317 317 }
318 318
319 319 return templateutil.mappinggenerator(entries)
320 320
321 321 def cleanpath(repo, path):
322 322 path = path.lstrip('/')
323 return pathutil.canonpath(repo.root, '', path)
323 auditor = pathutil.pathauditor(repo.root, realfs=False)
324 return pathutil.canonpath(repo.root, '', path, auditor=auditor)
324 325
325 326 def changectx(repo, req):
326 327 changeid = "tip"
327 328 if 'node' in req.qsparams:
328 329 changeid = req.qsparams['node']
329 330 ipos = changeid.find(':')
330 331 if ipos != -1:
331 332 changeid = changeid[(ipos + 1):]
332 333
333 334 return scmutil.revsymbol(repo, changeid)
334 335
335 336 def basechangectx(repo, req):
336 337 if 'node' in req.qsparams:
337 338 changeid = req.qsparams['node']
338 339 ipos = changeid.find(':')
339 340 if ipos != -1:
340 341 changeid = changeid[:ipos]
341 342 return scmutil.revsymbol(repo, changeid)
342 343
343 344 return None
344 345
345 346 def filectx(repo, req):
346 347 if 'file' not in req.qsparams:
347 348 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
348 349 path = cleanpath(repo, req.qsparams['file'])
349 350 if 'node' in req.qsparams:
350 351 changeid = req.qsparams['node']
351 352 elif 'filenode' in req.qsparams:
352 353 changeid = req.qsparams['filenode']
353 354 else:
354 355 raise ErrorResponse(HTTP_NOT_FOUND, 'node or filenode not given')
355 356 try:
356 357 fctx = scmutil.revsymbol(repo, changeid)[path]
357 358 except error.RepoError:
358 359 fctx = repo.filectx(path, fileid=changeid)
359 360
360 361 return fctx
361 362
362 363 def linerange(req):
363 364 linerange = req.qsparams.getall('linerange')
364 365 if not linerange:
365 366 return None
366 367 if len(linerange) > 1:
367 368 raise ErrorResponse(HTTP_BAD_REQUEST,
368 369 'redundant linerange parameter')
369 370 try:
370 371 fromline, toline = map(int, linerange[0].split(':', 1))
371 372 except ValueError:
372 373 raise ErrorResponse(HTTP_BAD_REQUEST,
373 374 'invalid linerange parameter')
374 375 try:
375 376 return util.processlinerange(fromline, toline)
376 377 except error.ParseError as exc:
377 378 raise ErrorResponse(HTTP_BAD_REQUEST, pycompat.bytestr(exc))
378 379
379 380 def formatlinerange(fromline, toline):
380 381 return '%d:%d' % (fromline + 1, toline)
381 382
382 383 def _succsandmarkersgen(context, mapping):
383 384 repo = context.resource(mapping, 'repo')
384 385 itemmappings = templatekw.showsuccsandmarkers(context, mapping)
385 386 for item in itemmappings.tovalue(context, mapping):
386 387 item['successors'] = _siblings(repo[successor]
387 388 for successor in item['successors'])
388 389 yield item
389 390
390 391 def succsandmarkers(context, mapping):
391 392 return templateutil.mappinggenerator(_succsandmarkersgen, args=(mapping,))
392 393
393 394 # teach templater succsandmarkers is switched to (context, mapping) API
394 395 succsandmarkers._requires = {'repo', 'ctx'}
395 396
396 397 def _whyunstablegen(context, mapping):
397 398 repo = context.resource(mapping, 'repo')
398 399 ctx = context.resource(mapping, 'ctx')
399 400
400 401 entries = obsutil.whyunstable(repo, ctx)
401 402 for entry in entries:
402 403 if entry.get('divergentnodes'):
403 404 entry['divergentnodes'] = _siblings(entry['divergentnodes'])
404 405 yield entry
405 406
406 407 def whyunstable(context, mapping):
407 408 return templateutil.mappinggenerator(_whyunstablegen, args=(mapping,))
408 409
409 410 whyunstable._requires = {'repo', 'ctx'}
410 411
411 412 # helper to mark a function as a new-style template keyword; can be removed
412 413 # once old-style function gets unsupported and new-style becomes the default
413 414 def _kwfunc(f):
414 415 f._requires = ()
415 416 return f
416 417
417 418 def commonentry(repo, ctx):
418 419 node = ctx.node()
419 420 return {
420 421 # TODO: perhaps ctx.changectx() should be assigned if ctx is a
421 422 # filectx, but I'm not pretty sure if that would always work because
422 423 # fctx.parents() != fctx.changectx.parents() for example.
423 424 'ctx': ctx,
424 425 'rev': ctx.rev(),
425 426 'node': hex(node),
426 427 'author': ctx.user(),
427 428 'desc': ctx.description(),
428 429 'date': ctx.date(),
429 430 'extra': ctx.extra(),
430 431 'phase': ctx.phasestr(),
431 432 'obsolete': ctx.obsolete(),
432 433 'succsandmarkers': succsandmarkers,
433 434 'instabilities': templateutil.hybridlist(ctx.instabilities(),
434 435 name='instability'),
435 436 'whyunstable': whyunstable,
436 437 'branch': nodebranchnodefault(ctx),
437 438 'inbranch': nodeinbranch(repo, ctx),
438 439 'branches': nodebranchdict(repo, ctx),
439 440 'tags': nodetagsdict(repo, node),
440 441 'bookmarks': nodebookmarksdict(repo, node),
441 442 'parent': _kwfunc(lambda context, mapping: parents(ctx)),
442 443 'child': _kwfunc(lambda context, mapping: children(ctx)),
443 444 }
444 445
445 446 def changelistentry(web, ctx):
446 447 '''Obtain a dictionary to be used for entries in a changelist.
447 448
448 449 This function is called when producing items for the "entries" list passed
449 450 to the "shortlog" and "changelog" templates.
450 451 '''
451 452 repo = web.repo
452 453 rev = ctx.rev()
453 454 n = ctx.node()
454 455 showtags = showtag(repo, 'changelogtag', n)
455 456 files = listfilediffs(ctx.files(), n, web.maxfiles)
456 457
457 458 entry = commonentry(repo, ctx)
458 459 entry.update(
459 460 allparents=_kwfunc(lambda context, mapping: parents(ctx)),
460 461 parent=_kwfunc(lambda context, mapping: parents(ctx, rev - 1)),
461 462 child=_kwfunc(lambda context, mapping: children(ctx, rev + 1)),
462 463 changelogtag=showtags,
463 464 files=files,
464 465 )
465 466 return entry
466 467
467 468 def changelistentries(web, revs, maxcount, parityfn):
468 469 """Emit up to N records for an iterable of revisions."""
469 470 repo = web.repo
470 471
471 472 count = 0
472 473 for rev in revs:
473 474 if count >= maxcount:
474 475 break
475 476
476 477 count += 1
477 478
478 479 entry = changelistentry(web, repo[rev])
479 480 entry['parity'] = next(parityfn)
480 481
481 482 yield entry
482 483
483 484 def symrevorshortnode(req, ctx):
484 485 if 'node' in req.qsparams:
485 486 return templatefilters.revescape(req.qsparams['node'])
486 487 else:
487 488 return short(ctx.node())
488 489
489 490 def _listfilesgen(context, ctx, stripecount):
490 491 parity = paritygen(stripecount)
491 492 for blockno, f in enumerate(ctx.files()):
492 493 template = 'filenodelink' if f in ctx else 'filenolink'
493 494 yield context.process(template, {
494 495 'node': ctx.hex(),
495 496 'file': f,
496 497 'blockno': blockno + 1,
497 498 'parity': next(parity),
498 499 })
499 500
500 501 def changesetentry(web, ctx):
501 502 '''Obtain a dictionary to be used to render the "changeset" template.'''
502 503
503 504 showtags = showtag(web.repo, 'changesettag', ctx.node())
504 505 showbookmarks = showbookmark(web.repo, 'changesetbookmark', ctx.node())
505 506 showbranch = nodebranchnodefault(ctx)
506 507
507 508 basectx = basechangectx(web.repo, web.req)
508 509 if basectx is None:
509 510 basectx = ctx.p1()
510 511
511 512 style = web.config('web', 'style')
512 513 if 'style' in web.req.qsparams:
513 514 style = web.req.qsparams['style']
514 515
515 516 diff = diffs(web, ctx, basectx, None, style)
516 517
517 518 parity = paritygen(web.stripecount)
518 519 diffstatsgen = diffstatgen(web.repo.ui, ctx, basectx)
519 520 diffstats = diffstat(ctx, diffstatsgen, parity)
520 521
521 522 return dict(
522 523 diff=diff,
523 524 symrev=symrevorshortnode(web.req, ctx),
524 525 basenode=basectx.hex(),
525 526 changesettag=showtags,
526 527 changesetbookmark=showbookmarks,
527 528 changesetbranch=showbranch,
528 529 files=templateutil.mappedgenerator(_listfilesgen,
529 530 args=(ctx, web.stripecount)),
530 531 diffsummary=_kwfunc(lambda context, mapping: diffsummary(diffstatsgen)),
531 532 diffstat=diffstats,
532 533 archives=web.archivelist(ctx.hex()),
533 534 **pycompat.strkwargs(commonentry(web.repo, ctx)))
534 535
535 536 def _listfilediffsgen(context, files, node, max):
536 537 for f in files[:max]:
537 538 yield context.process('filedifflink', {'node': hex(node), 'file': f})
538 539 if len(files) > max:
539 540 yield context.process('fileellipses', {})
540 541
541 542 def listfilediffs(files, node, max):
542 543 return templateutil.mappedgenerator(_listfilediffsgen,
543 544 args=(files, node, max))
544 545
545 546 def _prettyprintdifflines(context, lines, blockno, lineidprefix):
546 547 for lineno, l in enumerate(lines, 1):
547 548 difflineno = "%d.%d" % (blockno, lineno)
548 549 if l.startswith('+'):
549 550 ltype = "difflineplus"
550 551 elif l.startswith('-'):
551 552 ltype = "difflineminus"
552 553 elif l.startswith('@'):
553 554 ltype = "difflineat"
554 555 else:
555 556 ltype = "diffline"
556 557 yield context.process(ltype, {
557 558 'line': l,
558 559 'lineno': lineno,
559 560 'lineid': lineidprefix + "l%s" % difflineno,
560 561 'linenumber': "% 8s" % difflineno,
561 562 })
562 563
563 564 def _diffsgen(context, repo, ctx, basectx, files, style, stripecount,
564 565 linerange, lineidprefix):
565 566 if files:
566 567 m = match.exact(repo.root, repo.getcwd(), files)
567 568 else:
568 569 m = match.always(repo.root, repo.getcwd())
569 570
570 571 diffopts = patch.diffopts(repo.ui, untrusted=True)
571 572 node1 = basectx.node()
572 573 node2 = ctx.node()
573 574 parity = paritygen(stripecount)
574 575
575 576 diffhunks = patch.diffhunks(repo, node1, node2, m, opts=diffopts)
576 577 for blockno, (fctx1, fctx2, header, hunks) in enumerate(diffhunks, 1):
577 578 if style != 'raw':
578 579 header = header[1:]
579 580 lines = [h + '\n' for h in header]
580 581 for hunkrange, hunklines in hunks:
581 582 if linerange is not None and hunkrange is not None:
582 583 s1, l1, s2, l2 = hunkrange
583 584 if not mdiff.hunkinrange((s2, l2), linerange):
584 585 continue
585 586 lines.extend(hunklines)
586 587 if lines:
587 588 l = templateutil.mappedgenerator(_prettyprintdifflines,
588 589 args=(lines, blockno,
589 590 lineidprefix))
590 591 yield {
591 592 'parity': next(parity),
592 593 'blockno': blockno,
593 594 'lines': l,
594 595 }
595 596
596 597 def diffs(web, ctx, basectx, files, style, linerange=None, lineidprefix=''):
597 598 args = (web.repo, ctx, basectx, files, style, web.stripecount,
598 599 linerange, lineidprefix)
599 600 return templateutil.mappinggenerator(_diffsgen, args=args, name='diffblock')
600 601
601 602 def _compline(type, leftlineno, leftline, rightlineno, rightline):
602 603 lineid = leftlineno and ("l%d" % leftlineno) or ''
603 604 lineid += rightlineno and ("r%d" % rightlineno) or ''
604 605 llno = '%d' % leftlineno if leftlineno else ''
605 606 rlno = '%d' % rightlineno if rightlineno else ''
606 607 return {
607 608 'type': type,
608 609 'lineid': lineid,
609 610 'leftlineno': leftlineno,
610 611 'leftlinenumber': "% 6s" % llno,
611 612 'leftline': leftline or '',
612 613 'rightlineno': rightlineno,
613 614 'rightlinenumber': "% 6s" % rlno,
614 615 'rightline': rightline or '',
615 616 }
616 617
617 618 def _getcompblockgen(context, leftlines, rightlines, opcodes):
618 619 for type, llo, lhi, rlo, rhi in opcodes:
619 620 len1 = lhi - llo
620 621 len2 = rhi - rlo
621 622 count = min(len1, len2)
622 623 for i in pycompat.xrange(count):
623 624 yield _compline(type=type,
624 625 leftlineno=llo + i + 1,
625 626 leftline=leftlines[llo + i],
626 627 rightlineno=rlo + i + 1,
627 628 rightline=rightlines[rlo + i])
628 629 if len1 > len2:
629 630 for i in pycompat.xrange(llo + count, lhi):
630 631 yield _compline(type=type,
631 632 leftlineno=i + 1,
632 633 leftline=leftlines[i],
633 634 rightlineno=None,
634 635 rightline=None)
635 636 elif len2 > len1:
636 637 for i in pycompat.xrange(rlo + count, rhi):
637 638 yield _compline(type=type,
638 639 leftlineno=None,
639 640 leftline=None,
640 641 rightlineno=i + 1,
641 642 rightline=rightlines[i])
642 643
643 644 def _getcompblock(leftlines, rightlines, opcodes):
644 645 args = (leftlines, rightlines, opcodes)
645 646 return templateutil.mappinggenerator(_getcompblockgen, args=args,
646 647 name='comparisonline')
647 648
648 649 def _comparegen(context, contextnum, leftlines, rightlines):
649 650 '''Generator function that provides side-by-side comparison data.'''
650 651 s = difflib.SequenceMatcher(None, leftlines, rightlines)
651 652 if contextnum < 0:
652 653 l = _getcompblock(leftlines, rightlines, s.get_opcodes())
653 654 yield {'lines': l}
654 655 else:
655 656 for oc in s.get_grouped_opcodes(n=contextnum):
656 657 l = _getcompblock(leftlines, rightlines, oc)
657 658 yield {'lines': l}
658 659
659 660 def compare(contextnum, leftlines, rightlines):
660 661 args = (contextnum, leftlines, rightlines)
661 662 return templateutil.mappinggenerator(_comparegen, args=args,
662 663 name='comparisonblock')
663 664
664 665 def diffstatgen(ui, ctx, basectx):
665 666 '''Generator function that provides the diffstat data.'''
666 667
667 668 diffopts = patch.diffopts(ui, {'noprefix': False})
668 669 stats = patch.diffstatdata(
669 670 util.iterlines(ctx.diff(basectx, opts=diffopts)))
670 671 maxname, maxtotal, addtotal, removetotal, binary = patch.diffstatsum(stats)
671 672 while True:
672 673 yield stats, maxname, maxtotal, addtotal, removetotal, binary
673 674
674 675 def diffsummary(statgen):
675 676 '''Return a short summary of the diff.'''
676 677
677 678 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
678 679 return _(' %d files changed, %d insertions(+), %d deletions(-)\n') % (
679 680 len(stats), addtotal, removetotal)
680 681
681 682 def _diffstattmplgen(context, ctx, statgen, parity):
682 683 stats, maxname, maxtotal, addtotal, removetotal, binary = next(statgen)
683 684 files = ctx.files()
684 685
685 686 def pct(i):
686 687 if maxtotal == 0:
687 688 return 0
688 689 return (float(i) / maxtotal) * 100
689 690
690 691 fileno = 0
691 692 for filename, adds, removes, isbinary in stats:
692 693 template = 'diffstatlink' if filename in files else 'diffstatnolink'
693 694 total = adds + removes
694 695 fileno += 1
695 696 yield context.process(template, {
696 697 'node': ctx.hex(),
697 698 'file': filename,
698 699 'fileno': fileno,
699 700 'total': total,
700 701 'addpct': pct(adds),
701 702 'removepct': pct(removes),
702 703 'parity': next(parity),
703 704 })
704 705
705 706 def diffstat(ctx, statgen, parity):
706 707 '''Return a diffstat template for each file in the diff.'''
707 708 args = (ctx, statgen, parity)
708 709 return templateutil.mappedgenerator(_diffstattmplgen, args=args)
709 710
710 711 class sessionvars(templateutil.wrapped):
711 712 def __init__(self, vars, start='?'):
712 713 self._start = start
713 714 self._vars = vars
714 715
715 716 def __getitem__(self, key):
716 717 return self._vars[key]
717 718
718 719 def __setitem__(self, key, value):
719 720 self._vars[key] = value
720 721
721 722 def __copy__(self):
722 723 return sessionvars(copy.copy(self._vars), self._start)
723 724
724 725 def contains(self, context, mapping, item):
725 726 item = templateutil.unwrapvalue(context, mapping, item)
726 727 return item in self._vars
727 728
728 729 def getmember(self, context, mapping, key):
729 730 key = templateutil.unwrapvalue(context, mapping, key)
730 731 return self._vars.get(key)
731 732
732 733 def getmin(self, context, mapping):
733 734 raise error.ParseError(_('not comparable'))
734 735
735 736 def getmax(self, context, mapping):
736 737 raise error.ParseError(_('not comparable'))
737 738
738 739 def filter(self, context, mapping, select):
739 740 # implement if necessary
740 741 raise error.ParseError(_('not filterable'))
741 742
742 743 def itermaps(self, context):
743 744 separator = self._start
744 745 for key, value in sorted(self._vars.iteritems()):
745 746 yield {'name': key,
746 747 'value': pycompat.bytestr(value),
747 748 'separator': separator,
748 749 }
749 750 separator = '&'
750 751
751 752 def join(self, context, mapping, sep):
752 753 # could be '{separator}{name}={value|urlescape}'
753 754 raise error.ParseError(_('not displayable without template'))
754 755
755 756 def show(self, context, mapping):
756 757 return self.join(context, '')
757 758
758 759 def tobool(self, context, mapping):
759 760 return bool(self._vars)
760 761
761 762 def tovalue(self, context, mapping):
762 763 return self._vars
763 764
764 765 class wsgiui(uimod.ui):
765 766 # default termwidth breaks under mod_wsgi
766 767 def termwidth(self):
767 768 return 80
768 769
769 770 def getwebsubs(repo):
770 771 websubtable = []
771 772 websubdefs = repo.ui.configitems('websub')
772 773 # we must maintain interhg backwards compatibility
773 774 websubdefs += repo.ui.configitems('interhg')
774 775 for key, pattern in websubdefs:
775 776 # grab the delimiter from the character after the "s"
776 777 unesc = pattern[1:2]
777 778 delim = stringutil.reescape(unesc)
778 779
779 780 # identify portions of the pattern, taking care to avoid escaped
780 781 # delimiters. the replace format and flags are optional, but
781 782 # delimiters are required.
782 783 match = re.match(
783 784 br'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$'
784 785 % (delim, delim, delim), pattern)
785 786 if not match:
786 787 repo.ui.warn(_("websub: invalid pattern for %s: %s\n")
787 788 % (key, pattern))
788 789 continue
789 790
790 791 # we need to unescape the delimiter for regexp and format
791 792 delim_re = re.compile(br'(?<!\\)\\%s' % delim)
792 793 regexp = delim_re.sub(unesc, match.group(1))
793 794 format = delim_re.sub(unesc, match.group(2))
794 795
795 796 # the pattern allows for 6 regexp flags, so set them if necessary
796 797 flagin = match.group(3)
797 798 flags = 0
798 799 if flagin:
799 800 for flag in flagin.upper():
800 801 flags |= re.__dict__[flag]
801 802
802 803 try:
803 804 regexp = re.compile(regexp, flags)
804 805 websubtable.append((regexp, format))
805 806 except re.error:
806 807 repo.ui.warn(_("websub: invalid regexp for %s: %s\n")
807 808 % (key, regexp))
808 809 return websubtable
809 810
810 811 def getgraphnode(repo, ctx):
811 812 return (templatekw.getgraphnodecurrent(repo, ctx) +
812 813 templatekw.getgraphnodesymbol(ctx))
@@ -1,1791 +1,1792 b''
1 1 #require serve
2 2
3 3 hide outer repo and work in dir without '.hg'
4 4 $ hg init
5 5 $ mkdir dir
6 6 $ cd dir
7 7
8 8 Tests some basic hgwebdir functionality. Tests setting up paths and
9 9 collection, different forms of 404s and the subdirectory support.
10 10
11 11 $ mkdir webdir
12 12 $ cd webdir
13 13 $ hg init a
14 14 $ echo a > a/a
15 15 $ hg --cwd a ci -Ama -d'1 0'
16 16 adding a
17 17
18 18 create a mercurial queue repository
19 19
20 20 $ hg --cwd a qinit --config extensions.hgext.mq= -c
21 21 $ hg init b
22 22 $ echo b > b/b
23 23 $ hg --cwd b ci -Amb -d'2 0'
24 24 adding b
25 25
26 26 create a nested repository
27 27
28 28 $ cd b
29 29 $ hg init d
30 30 $ echo d > d/d
31 31 $ hg --cwd d ci -Amd -d'3 0'
32 32 adding d
33 33 $ cd ..
34 34 $ hg init c
35 35 $ echo c > c/c
36 36 $ hg --cwd c ci -Amc -d'3 0'
37 37 adding c
38 38
39 39 create a subdirectory containing repositories and subrepositories
40 40
41 41 $ mkdir notrepo
42 42 $ cd notrepo
43 43 $ hg init e
44 44 $ echo e > e/e
45 45 $ hg --cwd e ci -Ame -d'4 0'
46 46 adding e
47 47 $ hg init e/e2
48 48 $ echo e2 > e/e2/e2
49 49 $ hg --cwd e/e2 ci -Ame2 -d '4 0'
50 50 adding e2
51 51 $ hg init f
52 52 $ echo f > f/f
53 53 $ hg --cwd f ci -Amf -d'4 0'
54 54 adding f
55 55 $ hg init f/f2
56 56 $ echo f2 > f/f2/f2
57 57 $ hg --cwd f/f2 ci -Amf2 -d '4 0'
58 58 adding f2
59 59 $ echo 'f2 = f2' > f/.hgsub
60 60 $ hg -R f ci -Am 'add subrepo' -d'4 0'
61 61 adding .hgsub
62 62 $ cat >> f/.hg/hgrc << EOF
63 63 > [web]
64 64 > name = fancy name for repo f
65 65 > labels = foo, bar
66 66 > EOF
67 67 $ cd ..
68 68
69 69 add file under the directory which could be shadowed by another repository
70 70
71 71 $ mkdir notrepo/f/f3
72 72 $ echo f3/file > notrepo/f/f3/file
73 73 $ hg -R notrepo/f ci -Am 'f3/file'
74 74 adding f3/file
75 75 $ hg -R notrepo/f update null
76 76 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
77 77 $ hg init notrepo/f/f3
78 78 $ cat <<'EOF' > notrepo/f/f3/.hg/hgrc
79 79 > [web]
80 80 > hidden = true
81 81 > EOF
82 82
83 83 create repository without .hg/store
84 84
85 85 $ hg init nostore
86 86 $ rm -R nostore/.hg/store
87 87 $ root=`pwd`
88 88 $ cd ..
89 89
90 90 serve
91 91 $ cat > paths.conf <<EOF
92 92 > [paths]
93 93 > a=$root/a
94 94 > b=$root/b
95 95 > EOF
96 96 $ hg serve -p $HGPORT -d --pid-file=hg.pid --webdir-conf paths.conf \
97 97 > -A access-paths.log -E error-paths-1.log
98 98 $ cat hg.pid >> $DAEMON_PIDS
99 99
100 100 should give a 404 - file does not exist
101 101
102 102 $ get-with-headers.py localhost:$HGPORT 'a/file/tip/bork?style=raw'
103 103 404 Not Found
104 104
105 105
106 106 error: bork@8580ff50825a: not found in manifest
107 107 [1]
108 108
109 109 should succeed
110 110
111 111 $ get-with-headers.py localhost:$HGPORT '?style=raw'
112 112 200 Script output follows
113 113
114 114
115 115 /a/
116 116 /b/
117 117
118 118 $ get-with-headers.py localhost:$HGPORT '?style=json'
119 119 200 Script output follows
120 120
121 121 {
122 122 "entries": [{
123 123 "name": "a",
124 124 "description": "unknown",
125 125 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
126 126 "lastchange": [*, *], (glob)
127 127 "labels": []
128 128 }, {
129 129 "name": "b",
130 130 "description": "unknown",
131 131 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
132 132 "lastchange": [*, *], (glob)
133 133 "labels": []
134 134 }]
135 135 } (no-eol)
136 136
137 137 $ get-with-headers.py localhost:$HGPORT 'a/file/tip/a?style=raw'
138 138 200 Script output follows
139 139
140 140 a
141 141 $ get-with-headers.py localhost:$HGPORT 'b/file/tip/b?style=raw'
142 142 200 Script output follows
143 143
144 144 b
145 145
146 146 should give a 404 - repo is not published
147 147
148 148 $ get-with-headers.py localhost:$HGPORT 'c/file/tip/c?style=raw'
149 149 404 Not Found
150 150
151 151
152 152 error: repository c/file/tip/c not found
153 153 [1]
154 154
155 155 atom-log without basedir
156 156
157 157 $ get-with-headers.py localhost:$HGPORT 'a/atom-log' | grep '<link'
158 158 <link rel="self" href="http://*:$HGPORT/a/atom-log"/> (glob)
159 159 <link rel="alternate" href="http://*:$HGPORT/a/"/> (glob)
160 160 <link href="http://*:$HGPORT/a/rev/8580ff50825a"/> (glob)
161 161
162 162 rss-log without basedir
163 163
164 164 $ get-with-headers.py localhost:$HGPORT 'a/rss-log' | grep '<guid'
165 165 <guid isPermaLink="true">http://*:$HGPORT/a/rev/8580ff50825a</guid> (glob)
166 166 $ cat > paths.conf <<EOF
167 167 > [paths]
168 168 > t/a/=$root/a
169 169 > b=$root/b
170 170 > coll=$root/*
171 171 > rcoll=$root/**
172 172 > star=*
173 173 > starstar=**
174 174 > astar=webdir/a/*
175 175 > EOF
176 176 $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
177 177 > -A access-paths.log -E error-paths-2.log
178 178 $ cat hg.pid >> $DAEMON_PIDS
179 179
180 180 should succeed, slashy names
181 181
182 182 $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
183 183 200 Script output follows
184 184
185 185
186 186 /t/a/
187 187 /b/
188 188 /coll/a/
189 189 /coll/a/.hg/patches/
190 190 /coll/b/
191 191 /coll/c/
192 192 /coll/notrepo/e/
193 193 /coll/notrepo/f/
194 194 /rcoll/a/
195 195 /rcoll/a/.hg/patches/
196 196 /rcoll/b/
197 197 /rcoll/b/d/
198 198 /rcoll/c/
199 199 /rcoll/notrepo/e/
200 200 /rcoll/notrepo/e/e2/
201 201 /rcoll/notrepo/f/
202 202 /rcoll/notrepo/f/f2/
203 203 /star/webdir/a/
204 204 /star/webdir/a/.hg/patches/
205 205 /star/webdir/b/
206 206 /star/webdir/c/
207 207 /star/webdir/notrepo/e/
208 208 /star/webdir/notrepo/f/
209 209 /starstar/webdir/a/
210 210 /starstar/webdir/a/.hg/patches/
211 211 /starstar/webdir/b/
212 212 /starstar/webdir/b/d/
213 213 /starstar/webdir/c/
214 214 /starstar/webdir/notrepo/e/
215 215 /starstar/webdir/notrepo/e/e2/
216 216 /starstar/webdir/notrepo/f/
217 217 /starstar/webdir/notrepo/f/f2/
218 218 /astar/
219 219 /astar/.hg/patches/
220 220
221 221
222 222 $ get-with-headers.py localhost:$HGPORT1 '?style=json'
223 223 200 Script output follows
224 224
225 225 {
226 226 "entries": [{
227 227 "name": "t/a",
228 228 "description": "unknown",
229 229 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
230 230 "lastchange": [*, *], (glob)
231 231 "labels": []
232 232 }, {
233 233 "name": "b",
234 234 "description": "unknown",
235 235 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
236 236 "lastchange": [*, *], (glob)
237 237 "labels": []
238 238 }, {
239 239 "name": "coll/a",
240 240 "description": "unknown",
241 241 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
242 242 "lastchange": [*, *], (glob)
243 243 "labels": []
244 244 }, {
245 245 "name": "coll/a/.hg/patches",
246 246 "description": "unknown",
247 247 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
248 248 "lastchange": [*, *], (glob)
249 249 "labels": []
250 250 }, {
251 251 "name": "coll/b",
252 252 "description": "unknown",
253 253 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
254 254 "lastchange": [*, *], (glob)
255 255 "labels": []
256 256 }, {
257 257 "name": "coll/c",
258 258 "description": "unknown",
259 259 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
260 260 "lastchange": [*, *], (glob)
261 261 "labels": []
262 262 }, {
263 263 "name": "coll/notrepo/e",
264 264 "description": "unknown",
265 265 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
266 266 "lastchange": [*, *], (glob)
267 267 "labels": []
268 268 }, {
269 269 "name": "fancy name for repo f",
270 270 "description": "unknown",
271 271 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
272 272 "lastchange": [*, *], (glob)
273 273 "labels": ["foo", "bar"]
274 274 }, {
275 275 "name": "rcoll/a",
276 276 "description": "unknown",
277 277 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
278 278 "lastchange": [*, *], (glob)
279 279 "labels": []
280 280 }, {
281 281 "name": "rcoll/a/.hg/patches",
282 282 "description": "unknown",
283 283 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
284 284 "lastchange": [*, *], (glob)
285 285 "labels": []
286 286 }, {
287 287 "name": "rcoll/b",
288 288 "description": "unknown",
289 289 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
290 290 "lastchange": [*, *], (glob)
291 291 "labels": []
292 292 }, {
293 293 "name": "rcoll/b/d",
294 294 "description": "unknown",
295 295 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
296 296 "lastchange": [*, *], (glob)
297 297 "labels": []
298 298 }, {
299 299 "name": "rcoll/c",
300 300 "description": "unknown",
301 301 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
302 302 "lastchange": [*, *], (glob)
303 303 "labels": []
304 304 }, {
305 305 "name": "rcoll/notrepo/e",
306 306 "description": "unknown",
307 307 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
308 308 "lastchange": [*, *], (glob)
309 309 "labels": []
310 310 }, {
311 311 "name": "rcoll/notrepo/e/e2",
312 312 "description": "unknown",
313 313 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
314 314 "lastchange": [*, *], (glob)
315 315 "labels": []
316 316 }, {
317 317 "name": "fancy name for repo f",
318 318 "description": "unknown",
319 319 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
320 320 "lastchange": [*, *], (glob)
321 321 "labels": ["foo", "bar"]
322 322 }, {
323 323 "name": "rcoll/notrepo/f/f2",
324 324 "description": "unknown",
325 325 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
326 326 "lastchange": [*, *], (glob)
327 327 "labels": []
328 328 }, {
329 329 "name": "star/webdir/a",
330 330 "description": "unknown",
331 331 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
332 332 "lastchange": [*, *], (glob)
333 333 "labels": []
334 334 }, {
335 335 "name": "star/webdir/a/.hg/patches",
336 336 "description": "unknown",
337 337 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
338 338 "lastchange": [*, *], (glob)
339 339 "labels": []
340 340 }, {
341 341 "name": "star/webdir/b",
342 342 "description": "unknown",
343 343 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
344 344 "lastchange": [*, *], (glob)
345 345 "labels": []
346 346 }, {
347 347 "name": "star/webdir/c",
348 348 "description": "unknown",
349 349 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
350 350 "lastchange": [*, *], (glob)
351 351 "labels": []
352 352 }, {
353 353 "name": "star/webdir/notrepo/e",
354 354 "description": "unknown",
355 355 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
356 356 "lastchange": [*, *], (glob)
357 357 "labels": []
358 358 }, {
359 359 "name": "fancy name for repo f",
360 360 "description": "unknown",
361 361 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
362 362 "lastchange": [*, *], (glob)
363 363 "labels": ["foo", "bar"]
364 364 }, {
365 365 "name": "starstar/webdir/a",
366 366 "description": "unknown",
367 367 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
368 368 "lastchange": [*, *], (glob)
369 369 "labels": []
370 370 }, {
371 371 "name": "starstar/webdir/a/.hg/patches",
372 372 "description": "unknown",
373 373 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
374 374 "lastchange": [*, *], (glob)
375 375 "labels": []
376 376 }, {
377 377 "name": "starstar/webdir/b",
378 378 "description": "unknown",
379 379 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
380 380 "lastchange": [*, *], (glob)
381 381 "labels": []
382 382 }, {
383 383 "name": "starstar/webdir/b/d",
384 384 "description": "unknown",
385 385 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
386 386 "lastchange": [*, *], (glob)
387 387 "labels": []
388 388 }, {
389 389 "name": "starstar/webdir/c",
390 390 "description": "unknown",
391 391 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
392 392 "lastchange": [*, *], (glob)
393 393 "labels": []
394 394 }, {
395 395 "name": "starstar/webdir/notrepo/e",
396 396 "description": "unknown",
397 397 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
398 398 "lastchange": [*, *], (glob)
399 399 "labels": []
400 400 }, {
401 401 "name": "starstar/webdir/notrepo/e/e2",
402 402 "description": "unknown",
403 403 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
404 404 "lastchange": [*, *], (glob)
405 405 "labels": []
406 406 }, {
407 407 "name": "fancy name for repo f",
408 408 "description": "unknown",
409 409 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
410 410 "lastchange": [*, *], (glob)
411 411 "labels": ["foo", "bar"]
412 412 }, {
413 413 "name": "starstar/webdir/notrepo/f/f2",
414 414 "description": "unknown",
415 415 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
416 416 "lastchange": [*, *], (glob)
417 417 "labels": []
418 418 }, {
419 419 "name": "astar",
420 420 "description": "unknown",
421 421 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
422 422 "lastchange": [*, *], (glob)
423 423 "labels": []
424 424 }, {
425 425 "name": "astar/.hg/patches",
426 426 "description": "unknown",
427 427 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
428 428 "lastchange": [*, *], (glob)
429 429 "labels": []
430 430 }]
431 431 } (no-eol)
432 432
433 433 $ get-with-headers.py localhost:$HGPORT1 '?style=paper'
434 434 200 Script output follows
435 435
436 436 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
437 437 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
438 438 <head>
439 439 <link rel="icon" href="/static/hgicon.png" type="image/png" />
440 440 <meta name="robots" content="index, nofollow" />
441 441 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
442 442 <script type="text/javascript" src="/static/mercurial.js"></script>
443 443
444 444 <title>Mercurial repositories index</title>
445 445 </head>
446 446 <body>
447 447
448 448 <div class="container">
449 449 <div class="menu">
450 450 <a href="https://mercurial-scm.org/">
451 451 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
452 452 </div>
453 453 <div class="main">
454 454 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
455 455
456 456 <table class="bigtable">
457 457 <thead>
458 458 <tr>
459 459 <th><a href="?sort=name">Name</a></th>
460 460 <th><a href="?sort=description">Description</a></th>
461 461 <th><a href="?sort=contact">Contact</a></th>
462 462 <th><a href="?sort=lastchange">Last modified</a></th>
463 463 <th>&nbsp;</th>
464 464 <th>&nbsp;</th>
465 465 </tr>
466 466 </thead>
467 467 <tbody class="stripes2">
468 468
469 469 <tr>
470 470 <td><a href="/t/a/?style=paper">t/a</a></td>
471 471 <td>unknown</td>
472 472 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
473 473 <td class="age">*</td> (glob)
474 474 <td class="indexlinks"></td>
475 475 <td>
476 476 <a href="/t/a/atom-log" title="subscribe to repository atom feed">
477 477 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
478 478 </a>
479 479 </td>
480 480 </tr>
481 481
482 482 <tr>
483 483 <td><a href="/b/?style=paper">b</a></td>
484 484 <td>unknown</td>
485 485 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
486 486 <td class="age">*</td> (glob)
487 487 <td class="indexlinks"></td>
488 488 <td>
489 489 <a href="/b/atom-log" title="subscribe to repository atom feed">
490 490 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
491 491 </a>
492 492 </td>
493 493 </tr>
494 494
495 495 <tr>
496 496 <td><a href="/coll/a/?style=paper">coll/a</a></td>
497 497 <td>unknown</td>
498 498 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
499 499 <td class="age">*</td> (glob)
500 500 <td class="indexlinks"></td>
501 501 <td>
502 502 <a href="/coll/a/atom-log" title="subscribe to repository atom feed">
503 503 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
504 504 </a>
505 505 </td>
506 506 </tr>
507 507
508 508 <tr>
509 509 <td><a href="/coll/a/.hg/patches/?style=paper">coll/a/.hg/patches</a></td>
510 510 <td>unknown</td>
511 511 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
512 512 <td class="age">*</td> (glob)
513 513 <td class="indexlinks"></td>
514 514 <td>
515 515 <a href="/coll/a/.hg/patches/atom-log" title="subscribe to repository atom feed">
516 516 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
517 517 </a>
518 518 </td>
519 519 </tr>
520 520
521 521 <tr>
522 522 <td><a href="/coll/b/?style=paper">coll/b</a></td>
523 523 <td>unknown</td>
524 524 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
525 525 <td class="age">*</td> (glob)
526 526 <td class="indexlinks"></td>
527 527 <td>
528 528 <a href="/coll/b/atom-log" title="subscribe to repository atom feed">
529 529 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
530 530 </a>
531 531 </td>
532 532 </tr>
533 533
534 534 <tr>
535 535 <td><a href="/coll/c/?style=paper">coll/c</a></td>
536 536 <td>unknown</td>
537 537 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
538 538 <td class="age">*</td> (glob)
539 539 <td class="indexlinks"></td>
540 540 <td>
541 541 <a href="/coll/c/atom-log" title="subscribe to repository atom feed">
542 542 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
543 543 </a>
544 544 </td>
545 545 </tr>
546 546
547 547 <tr>
548 548 <td><a href="/coll/notrepo/e/?style=paper">coll/notrepo/e</a></td>
549 549 <td>unknown</td>
550 550 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
551 551 <td class="age">*</td> (glob)
552 552 <td class="indexlinks"></td>
553 553 <td>
554 554 <a href="/coll/notrepo/e/atom-log" title="subscribe to repository atom feed">
555 555 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
556 556 </a>
557 557 </td>
558 558 </tr>
559 559
560 560 <tr>
561 561 <td><a href="/coll/notrepo/f/?style=paper">fancy name for repo f</a></td>
562 562 <td>unknown</td>
563 563 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
564 564 <td class="age">*</td> (glob)
565 565 <td class="indexlinks"></td>
566 566 <td>
567 567 <a href="/coll/notrepo/f/atom-log" title="subscribe to repository atom feed">
568 568 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
569 569 </a>
570 570 </td>
571 571 </tr>
572 572
573 573 <tr>
574 574 <td><a href="/rcoll/a/?style=paper">rcoll/a</a></td>
575 575 <td>unknown</td>
576 576 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
577 577 <td class="age">*</td> (glob)
578 578 <td class="indexlinks"></td>
579 579 <td>
580 580 <a href="/rcoll/a/atom-log" title="subscribe to repository atom feed">
581 581 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
582 582 </a>
583 583 </td>
584 584 </tr>
585 585
586 586 <tr>
587 587 <td><a href="/rcoll/a/.hg/patches/?style=paper">rcoll/a/.hg/patches</a></td>
588 588 <td>unknown</td>
589 589 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
590 590 <td class="age">*</td> (glob)
591 591 <td class="indexlinks"></td>
592 592 <td>
593 593 <a href="/rcoll/a/.hg/patches/atom-log" title="subscribe to repository atom feed">
594 594 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
595 595 </a>
596 596 </td>
597 597 </tr>
598 598
599 599 <tr>
600 600 <td><a href="/rcoll/b/?style=paper">rcoll/b</a></td>
601 601 <td>unknown</td>
602 602 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
603 603 <td class="age">*</td> (glob)
604 604 <td class="indexlinks"></td>
605 605 <td>
606 606 <a href="/rcoll/b/atom-log" title="subscribe to repository atom feed">
607 607 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
608 608 </a>
609 609 </td>
610 610 </tr>
611 611
612 612 <tr>
613 613 <td><a href="/rcoll/b/d/?style=paper">rcoll/b/d</a></td>
614 614 <td>unknown</td>
615 615 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
616 616 <td class="age">*</td> (glob)
617 617 <td class="indexlinks"></td>
618 618 <td>
619 619 <a href="/rcoll/b/d/atom-log" title="subscribe to repository atom feed">
620 620 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
621 621 </a>
622 622 </td>
623 623 </tr>
624 624
625 625 <tr>
626 626 <td><a href="/rcoll/c/?style=paper">rcoll/c</a></td>
627 627 <td>unknown</td>
628 628 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
629 629 <td class="age">*</td> (glob)
630 630 <td class="indexlinks"></td>
631 631 <td>
632 632 <a href="/rcoll/c/atom-log" title="subscribe to repository atom feed">
633 633 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
634 634 </a>
635 635 </td>
636 636 </tr>
637 637
638 638 <tr>
639 639 <td><a href="/rcoll/notrepo/e/?style=paper">rcoll/notrepo/e</a></td>
640 640 <td>unknown</td>
641 641 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
642 642 <td class="age">*</td> (glob)
643 643 <td class="indexlinks"></td>
644 644 <td>
645 645 <a href="/rcoll/notrepo/e/atom-log" title="subscribe to repository atom feed">
646 646 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
647 647 </a>
648 648 </td>
649 649 </tr>
650 650
651 651 <tr>
652 652 <td><a href="/rcoll/notrepo/e/e2/?style=paper">rcoll/notrepo/e/e2</a></td>
653 653 <td>unknown</td>
654 654 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
655 655 <td class="age">*</td> (glob)
656 656 <td class="indexlinks"></td>
657 657 <td>
658 658 <a href="/rcoll/notrepo/e/e2/atom-log" title="subscribe to repository atom feed">
659 659 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
660 660 </a>
661 661 </td>
662 662 </tr>
663 663
664 664 <tr>
665 665 <td><a href="/rcoll/notrepo/f/?style=paper">fancy name for repo f</a></td>
666 666 <td>unknown</td>
667 667 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
668 668 <td class="age">*</td> (glob)
669 669 <td class="indexlinks"></td>
670 670 <td>
671 671 <a href="/rcoll/notrepo/f/atom-log" title="subscribe to repository atom feed">
672 672 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
673 673 </a>
674 674 </td>
675 675 </tr>
676 676
677 677 <tr>
678 678 <td><a href="/rcoll/notrepo/f/f2/?style=paper">rcoll/notrepo/f/f2</a></td>
679 679 <td>unknown</td>
680 680 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
681 681 <td class="age">*</td> (glob)
682 682 <td class="indexlinks"></td>
683 683 <td>
684 684 <a href="/rcoll/notrepo/f/f2/atom-log" title="subscribe to repository atom feed">
685 685 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
686 686 </a>
687 687 </td>
688 688 </tr>
689 689
690 690 <tr>
691 691 <td><a href="/star/webdir/a/?style=paper">star/webdir/a</a></td>
692 692 <td>unknown</td>
693 693 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
694 694 <td class="age">*</td> (glob)
695 695 <td class="indexlinks"></td>
696 696 <td>
697 697 <a href="/star/webdir/a/atom-log" title="subscribe to repository atom feed">
698 698 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
699 699 </a>
700 700 </td>
701 701 </tr>
702 702
703 703 <tr>
704 704 <td><a href="/star/webdir/a/.hg/patches/?style=paper">star/webdir/a/.hg/patches</a></td>
705 705 <td>unknown</td>
706 706 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
707 707 <td class="age">*</td> (glob)
708 708 <td class="indexlinks"></td>
709 709 <td>
710 710 <a href="/star/webdir/a/.hg/patches/atom-log" title="subscribe to repository atom feed">
711 711 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
712 712 </a>
713 713 </td>
714 714 </tr>
715 715
716 716 <tr>
717 717 <td><a href="/star/webdir/b/?style=paper">star/webdir/b</a></td>
718 718 <td>unknown</td>
719 719 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
720 720 <td class="age">*</td> (glob)
721 721 <td class="indexlinks"></td>
722 722 <td>
723 723 <a href="/star/webdir/b/atom-log" title="subscribe to repository atom feed">
724 724 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
725 725 </a>
726 726 </td>
727 727 </tr>
728 728
729 729 <tr>
730 730 <td><a href="/star/webdir/c/?style=paper">star/webdir/c</a></td>
731 731 <td>unknown</td>
732 732 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
733 733 <td class="age">*</td> (glob)
734 734 <td class="indexlinks"></td>
735 735 <td>
736 736 <a href="/star/webdir/c/atom-log" title="subscribe to repository atom feed">
737 737 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
738 738 </a>
739 739 </td>
740 740 </tr>
741 741
742 742 <tr>
743 743 <td><a href="/star/webdir/notrepo/e/?style=paper">star/webdir/notrepo/e</a></td>
744 744 <td>unknown</td>
745 745 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
746 746 <td class="age">*</td> (glob)
747 747 <td class="indexlinks"></td>
748 748 <td>
749 749 <a href="/star/webdir/notrepo/e/atom-log" title="subscribe to repository atom feed">
750 750 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
751 751 </a>
752 752 </td>
753 753 </tr>
754 754
755 755 <tr>
756 756 <td><a href="/star/webdir/notrepo/f/?style=paper">fancy name for repo f</a></td>
757 757 <td>unknown</td>
758 758 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
759 759 <td class="age">*</td> (glob)
760 760 <td class="indexlinks"></td>
761 761 <td>
762 762 <a href="/star/webdir/notrepo/f/atom-log" title="subscribe to repository atom feed">
763 763 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
764 764 </a>
765 765 </td>
766 766 </tr>
767 767
768 768 <tr>
769 769 <td><a href="/starstar/webdir/a/?style=paper">starstar/webdir/a</a></td>
770 770 <td>unknown</td>
771 771 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
772 772 <td class="age">*</td> (glob)
773 773 <td class="indexlinks"></td>
774 774 <td>
775 775 <a href="/starstar/webdir/a/atom-log" title="subscribe to repository atom feed">
776 776 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
777 777 </a>
778 778 </td>
779 779 </tr>
780 780
781 781 <tr>
782 782 <td><a href="/starstar/webdir/a/.hg/patches/?style=paper">starstar/webdir/a/.hg/patches</a></td>
783 783 <td>unknown</td>
784 784 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
785 785 <td class="age">*</td> (glob)
786 786 <td class="indexlinks"></td>
787 787 <td>
788 788 <a href="/starstar/webdir/a/.hg/patches/atom-log" title="subscribe to repository atom feed">
789 789 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
790 790 </a>
791 791 </td>
792 792 </tr>
793 793
794 794 <tr>
795 795 <td><a href="/starstar/webdir/b/?style=paper">starstar/webdir/b</a></td>
796 796 <td>unknown</td>
797 797 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
798 798 <td class="age">*</td> (glob)
799 799 <td class="indexlinks"></td>
800 800 <td>
801 801 <a href="/starstar/webdir/b/atom-log" title="subscribe to repository atom feed">
802 802 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
803 803 </a>
804 804 </td>
805 805 </tr>
806 806
807 807 <tr>
808 808 <td><a href="/starstar/webdir/b/d/?style=paper">starstar/webdir/b/d</a></td>
809 809 <td>unknown</td>
810 810 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
811 811 <td class="age">*</td> (glob)
812 812 <td class="indexlinks"></td>
813 813 <td>
814 814 <a href="/starstar/webdir/b/d/atom-log" title="subscribe to repository atom feed">
815 815 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
816 816 </a>
817 817 </td>
818 818 </tr>
819 819
820 820 <tr>
821 821 <td><a href="/starstar/webdir/c/?style=paper">starstar/webdir/c</a></td>
822 822 <td>unknown</td>
823 823 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
824 824 <td class="age">*</td> (glob)
825 825 <td class="indexlinks"></td>
826 826 <td>
827 827 <a href="/starstar/webdir/c/atom-log" title="subscribe to repository atom feed">
828 828 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
829 829 </a>
830 830 </td>
831 831 </tr>
832 832
833 833 <tr>
834 834 <td><a href="/starstar/webdir/notrepo/e/?style=paper">starstar/webdir/notrepo/e</a></td>
835 835 <td>unknown</td>
836 836 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
837 837 <td class="age">*</td> (glob)
838 838 <td class="indexlinks"></td>
839 839 <td>
840 840 <a href="/starstar/webdir/notrepo/e/atom-log" title="subscribe to repository atom feed">
841 841 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
842 842 </a>
843 843 </td>
844 844 </tr>
845 845
846 846 <tr>
847 847 <td><a href="/starstar/webdir/notrepo/e/e2/?style=paper">starstar/webdir/notrepo/e/e2</a></td>
848 848 <td>unknown</td>
849 849 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
850 850 <td class="age">*</td> (glob)
851 851 <td class="indexlinks"></td>
852 852 <td>
853 853 <a href="/starstar/webdir/notrepo/e/e2/atom-log" title="subscribe to repository atom feed">
854 854 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
855 855 </a>
856 856 </td>
857 857 </tr>
858 858
859 859 <tr>
860 860 <td><a href="/starstar/webdir/notrepo/f/?style=paper">fancy name for repo f</a></td>
861 861 <td>unknown</td>
862 862 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
863 863 <td class="age">*</td> (glob)
864 864 <td class="indexlinks"></td>
865 865 <td>
866 866 <a href="/starstar/webdir/notrepo/f/atom-log" title="subscribe to repository atom feed">
867 867 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
868 868 </a>
869 869 </td>
870 870 </tr>
871 871
872 872 <tr>
873 873 <td><a href="/starstar/webdir/notrepo/f/f2/?style=paper">starstar/webdir/notrepo/f/f2</a></td>
874 874 <td>unknown</td>
875 875 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
876 876 <td class="age">*</td> (glob)
877 877 <td class="indexlinks"></td>
878 878 <td>
879 879 <a href="/starstar/webdir/notrepo/f/f2/atom-log" title="subscribe to repository atom feed">
880 880 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
881 881 </a>
882 882 </td>
883 883 </tr>
884 884
885 885 <tr>
886 886 <td><a href="/astar/?style=paper">astar</a></td>
887 887 <td>unknown</td>
888 888 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
889 889 <td class="age">*</td> (glob)
890 890 <td class="indexlinks"></td>
891 891 <td>
892 892 <a href="/astar/atom-log" title="subscribe to repository atom feed">
893 893 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
894 894 </a>
895 895 </td>
896 896 </tr>
897 897
898 898 <tr>
899 899 <td><a href="/astar/.hg/patches/?style=paper">astar/.hg/patches</a></td>
900 900 <td>unknown</td>
901 901 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
902 902 <td class="age">*</td> (glob)
903 903 <td class="indexlinks"></td>
904 904 <td>
905 905 <a href="/astar/.hg/patches/atom-log" title="subscribe to repository atom feed">
906 906 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
907 907 </a>
908 908 </td>
909 909 </tr>
910 910
911 911 </tbody>
912 912 </table>
913 913 </div>
914 914 </div>
915 915
916 916
917 917 </body>
918 918 </html>
919 919
920 920 $ get-with-headers.py localhost:$HGPORT1 't?style=raw'
921 921 200 Script output follows
922 922
923 923
924 924 /t/a/
925 925
926 926 $ get-with-headers.py localhost:$HGPORT1 't/?style=raw'
927 927 200 Script output follows
928 928
929 929
930 930 /t/a/
931 931
932 932 $ get-with-headers.py localhost:$HGPORT1 't/?style=paper'
933 933 200 Script output follows
934 934
935 935 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
936 936 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
937 937 <head>
938 938 <link rel="icon" href="/static/hgicon.png" type="image/png" />
939 939 <meta name="robots" content="index, nofollow" />
940 940 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
941 941 <script type="text/javascript" src="/static/mercurial.js"></script>
942 942
943 943 <title>Mercurial repositories index</title>
944 944 </head>
945 945 <body>
946 946
947 947 <div class="container">
948 948 <div class="menu">
949 949 <a href="https://mercurial-scm.org/">
950 950 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
951 951 </div>
952 952 <div class="main">
953 953 <h2 class="breadcrumb"><a href="/">Mercurial</a> &gt; <a href="/t">t</a> </h2>
954 954
955 955 <table class="bigtable">
956 956 <thead>
957 957 <tr>
958 958 <th><a href="?sort=name">Name</a></th>
959 959 <th><a href="?sort=description">Description</a></th>
960 960 <th><a href="?sort=contact">Contact</a></th>
961 961 <th><a href="?sort=lastchange">Last modified</a></th>
962 962 <th>&nbsp;</th>
963 963 <th>&nbsp;</th>
964 964 </tr>
965 965 </thead>
966 966 <tbody class="stripes2">
967 967
968 968 <tr>
969 969 <td><a href="/t/a/?style=paper">a</a></td>
970 970 <td>unknown</td>
971 971 <td>&#70;&#111;&#111;&#32;&#66;&#97;&#114;&#32;&#60;&#102;&#111;&#111;&#46;&#98;&#97;&#114;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;&#62;</td>
972 972 <td class="age">*</td> (glob)
973 973 <td class="indexlinks"></td>
974 974 <td>
975 975 <a href="/t/a/atom-log" title="subscribe to repository atom feed">
976 976 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
977 977 </a>
978 978 </td>
979 979 </tr>
980 980
981 981 </tbody>
982 982 </table>
983 983 </div>
984 984 </div>
985 985
986 986
987 987 </body>
988 988 </html>
989 989
990 990 $ get-with-headers.py localhost:$HGPORT1 't/a?style=atom'
991 991 200 Script output follows
992 992
993 993 <?xml version="1.0" encoding="ascii"?>
994 994 <feed xmlns="http://www.w3.org/2005/Atom">
995 995 <!-- Changelog -->
996 996 <id>http://*:$HGPORT1/t/a/</id> (glob)
997 997 <link rel="self" href="http://*:$HGPORT1/t/a/atom-log"/> (glob)
998 998 <link rel="alternate" href="http://*:$HGPORT1/t/a/"/> (glob)
999 999 <title>t/a Changelog</title>
1000 1000 <updated>1970-01-01T00:00:01+00:00</updated>
1001 1001
1002 1002 <entry>
1003 1003 <title>[default] a</title>
1004 1004 <id>http://*:$HGPORT1/t/a/#changeset-8580ff50825a50c8f716709acdf8de0deddcd6ab</id> (glob)
1005 1005 <link href="http://*:$HGPORT1/t/a/rev/8580ff50825a"/> (glob)
1006 1006 <author>
1007 1007 <name>test</name>
1008 1008 <email>&#116;&#101;&#115;&#116;</email>
1009 1009 </author>
1010 1010 <updated>1970-01-01T00:00:01+00:00</updated>
1011 1011 <published>1970-01-01T00:00:01+00:00</published>
1012 1012 <content type="xhtml">
1013 1013 <table xmlns="http://www.w3.org/1999/xhtml">
1014 1014 <tr>
1015 1015 <th style="text-align:left;">changeset</th>
1016 1016 <td>8580ff50825a</td>
1017 1017 </tr>
1018 1018 <tr>
1019 1019 <th style="text-align:left;">branch</th>
1020 1020 <td>default</td>
1021 1021 </tr>
1022 1022 <tr>
1023 1023 <th style="text-align:left;">bookmark</th>
1024 1024 <td></td>
1025 1025 </tr>
1026 1026 <tr>
1027 1027 <th style="text-align:left;">tag</th>
1028 1028 <td>tip</td>
1029 1029 </tr>
1030 1030 <tr>
1031 1031 <th style="text-align:left;">user</th>
1032 1032 <td>&#116;&#101;&#115;&#116;</td>
1033 1033 </tr>
1034 1034 <tr>
1035 1035 <th style="text-align:left;vertical-align:top;">description</th>
1036 1036 <td>a</td>
1037 1037 </tr>
1038 1038 <tr>
1039 1039 <th style="text-align:left;vertical-align:top;">files</th>
1040 1040 <td>a<br /></td>
1041 1041 </tr>
1042 1042 </table>
1043 1043 </content>
1044 1044 </entry>
1045 1045
1046 1046 </feed>
1047 1047 $ get-with-headers.py localhost:$HGPORT1 't/a/?style=atom'
1048 1048 200 Script output follows
1049 1049
1050 1050 <?xml version="1.0" encoding="ascii"?>
1051 1051 <feed xmlns="http://www.w3.org/2005/Atom">
1052 1052 <!-- Changelog -->
1053 1053 <id>http://*:$HGPORT1/t/a/</id> (glob)
1054 1054 <link rel="self" href="http://*:$HGPORT1/t/a/atom-log"/> (glob)
1055 1055 <link rel="alternate" href="http://*:$HGPORT1/t/a/"/> (glob)
1056 1056 <title>t/a Changelog</title>
1057 1057 <updated>1970-01-01T00:00:01+00:00</updated>
1058 1058
1059 1059 <entry>
1060 1060 <title>[default] a</title>
1061 1061 <id>http://*:$HGPORT1/t/a/#changeset-8580ff50825a50c8f716709acdf8de0deddcd6ab</id> (glob)
1062 1062 <link href="http://*:$HGPORT1/t/a/rev/8580ff50825a"/> (glob)
1063 1063 <author>
1064 1064 <name>test</name>
1065 1065 <email>&#116;&#101;&#115;&#116;</email>
1066 1066 </author>
1067 1067 <updated>1970-01-01T00:00:01+00:00</updated>
1068 1068 <published>1970-01-01T00:00:01+00:00</published>
1069 1069 <content type="xhtml">
1070 1070 <table xmlns="http://www.w3.org/1999/xhtml">
1071 1071 <tr>
1072 1072 <th style="text-align:left;">changeset</th>
1073 1073 <td>8580ff50825a</td>
1074 1074 </tr>
1075 1075 <tr>
1076 1076 <th style="text-align:left;">branch</th>
1077 1077 <td>default</td>
1078 1078 </tr>
1079 1079 <tr>
1080 1080 <th style="text-align:left;">bookmark</th>
1081 1081 <td></td>
1082 1082 </tr>
1083 1083 <tr>
1084 1084 <th style="text-align:left;">tag</th>
1085 1085 <td>tip</td>
1086 1086 </tr>
1087 1087 <tr>
1088 1088 <th style="text-align:left;">user</th>
1089 1089 <td>&#116;&#101;&#115;&#116;</td>
1090 1090 </tr>
1091 1091 <tr>
1092 1092 <th style="text-align:left;vertical-align:top;">description</th>
1093 1093 <td>a</td>
1094 1094 </tr>
1095 1095 <tr>
1096 1096 <th style="text-align:left;vertical-align:top;">files</th>
1097 1097 <td>a<br /></td>
1098 1098 </tr>
1099 1099 </table>
1100 1100 </content>
1101 1101 </entry>
1102 1102
1103 1103 </feed>
1104 1104 $ get-with-headers.py localhost:$HGPORT1 't/a/file/tip/a?style=raw'
1105 1105 200 Script output follows
1106 1106
1107 1107 a
1108 1108
1109 1109 Test [paths] '*' extension
1110 1110
1111 1111 $ get-with-headers.py localhost:$HGPORT1 'coll/?style=raw'
1112 1112 200 Script output follows
1113 1113
1114 1114
1115 1115 /coll/a/
1116 1116 /coll/a/.hg/patches/
1117 1117 /coll/b/
1118 1118 /coll/c/
1119 1119 /coll/notrepo/e/
1120 1120 /coll/notrepo/f/
1121 1121
1122 1122 $ get-with-headers.py localhost:$HGPORT1 'coll/a/file/tip/a?style=raw'
1123 1123 200 Script output follows
1124 1124
1125 1125 a
1126 1126
1127 1127 Test [paths] '**' extension
1128 1128
1129 1129 $ get-with-headers.py localhost:$HGPORT1 'rcoll/?style=raw'
1130 1130 200 Script output follows
1131 1131
1132 1132
1133 1133 /rcoll/a/
1134 1134 /rcoll/a/.hg/patches/
1135 1135 /rcoll/b/
1136 1136 /rcoll/b/d/
1137 1137 /rcoll/c/
1138 1138 /rcoll/notrepo/e/
1139 1139 /rcoll/notrepo/e/e2/
1140 1140 /rcoll/notrepo/f/
1141 1141 /rcoll/notrepo/f/f2/
1142 1142
1143 1143 $ get-with-headers.py localhost:$HGPORT1 'rcoll/b/d/file/tip/d?style=raw'
1144 1144 200 Script output follows
1145 1145
1146 1146 d
1147 1147
1148 1148 Test collapse = True
1149 1149
1150 1150 $ killdaemons.py
1151 1151 $ cat >> paths.conf <<EOF
1152 1152 > [web]
1153 1153 > collapse=true
1154 1154 > descend = true
1155 1155 > EOF
1156 1156 $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
1157 1157 > -A access-paths.log -E error-paths-3.log
1158 1158 $ cat hg.pid >> $DAEMON_PIDS
1159 1159 $ get-with-headers.py localhost:$HGPORT1 'coll/?style=raw'
1160 1160 200 Script output follows
1161 1161
1162 1162
1163 1163 /coll/a/
1164 1164 /coll/a/.hg/patches/
1165 1165 /coll/b/
1166 1166 /coll/c/
1167 1167 /coll/notrepo/
1168 1168
1169 1169 $ get-with-headers.py localhost:$HGPORT1 'coll/a/file/tip/a?style=raw'
1170 1170 200 Script output follows
1171 1171
1172 1172 a
1173 1173 $ get-with-headers.py localhost:$HGPORT1 'rcoll/?style=raw'
1174 1174 200 Script output follows
1175 1175
1176 1176
1177 1177 /rcoll/a/
1178 1178 /rcoll/a/.hg/patches/
1179 1179 /rcoll/b/
1180 1180 /rcoll/b/d/
1181 1181 /rcoll/c/
1182 1182 /rcoll/notrepo/
1183 1183
1184 1184 $ get-with-headers.py localhost:$HGPORT1 'rcoll/b/d/file/tip/d?style=raw'
1185 1185 200 Script output follows
1186 1186
1187 1187 d
1188 1188
1189 1189 Test intermediate directories
1190 1190
1191 1191 Hide the subrepo parent
1192 1192
1193 1193 $ cp $root/notrepo/f/.hg/hgrc $root/notrepo/f/.hg/hgrc.bak
1194 1194 $ cat >> $root/notrepo/f/.hg/hgrc << EOF
1195 1195 > [web]
1196 1196 > hidden = True
1197 1197 > EOF
1198 1198
1199 1199 $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/?style=raw'
1200 1200 200 Script output follows
1201 1201
1202 1202
1203 1203 /rcoll/notrepo/e/
1204 1204 /rcoll/notrepo/e/e2/
1205 1205
1206 1206
1207 1207 Subrepo parent not hidden
1208 1208 $ mv $root/notrepo/f/.hg/hgrc.bak $root/notrepo/f/.hg/hgrc
1209 1209
1210 1210 $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/?style=raw'
1211 1211 200 Script output follows
1212 1212
1213 1213
1214 1214 /rcoll/notrepo/e/
1215 1215 /rcoll/notrepo/e/e2/
1216 1216 /rcoll/notrepo/f/
1217 1217 /rcoll/notrepo/f/f2/
1218 1218
1219 1219
1220 1220 Test repositories inside intermediate directories
1221 1221
1222 1222 $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/e/file/tip/e?style=raw'
1223 1223 200 Script output follows
1224 1224
1225 1225 e
1226 1226
1227 1227 Test subrepositories inside intermediate directories
1228 1228
1229 1229 $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/f/f2/file/tip/f2?style=raw'
1230 1230 200 Script output follows
1231 1231
1232 1232 f2
1233 1233
1234 Test accessing file that is shadowed by another repository
1234 Test accessing file that could be shadowed by another repository if the URL
1235 path were audited as a working-directory path:
1235 1236
1236 1237 $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/f/file/tip/f3/file?style=raw'
1237 403 Forbidden
1238
1238 200 Script output follows
1239 1239
1240 error: path 'f3/file' is inside nested repo 'f3'
1241 [1]
1240 f3/file
1241
1242 Test accessing working-directory file that is shadowed by another repository
1242 1243
1243 1244 $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/f/file/ffffffffffff/f3/file?style=raw'
1244 1245 403 Forbidden
1245 1246
1246 1247
1247 1248 error: path 'f3/file' is inside nested repo 'f3'
1248 1249 [1]
1249 1250
1250 1251 Test accessing invalid paths:
1251 1252
1252 1253 $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/f/file/tip/..?style=raw'
1253 1254 403 Forbidden
1254 1255
1255 1256
1256 1257 error: .. not under root '$TESTTMP/dir/webdir/notrepo/f'
1257 1258 [1]
1258 1259
1259 1260 $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/f/file/tip/.hg/hgrc?style=raw'
1260 1261 403 Forbidden
1261 1262
1262 1263
1263 1264 error: path contains illegal component: .hg/hgrc
1264 1265 [1]
1265 1266
1266 1267 Test descend = False
1267 1268
1268 1269 $ killdaemons.py
1269 1270 $ cat >> paths.conf <<EOF
1270 1271 > descend=false
1271 1272 > EOF
1272 1273 $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
1273 1274 > -A access-paths.log -E error-paths-4.log
1274 1275 $ cat hg.pid >> $DAEMON_PIDS
1275 1276 $ get-with-headers.py localhost:$HGPORT1 'coll/?style=raw'
1276 1277 200 Script output follows
1277 1278
1278 1279
1279 1280 /coll/a/
1280 1281 /coll/b/
1281 1282 /coll/c/
1282 1283
1283 1284 $ get-with-headers.py localhost:$HGPORT1 'coll/a/file/tip/a?style=raw'
1284 1285 200 Script output follows
1285 1286
1286 1287 a
1287 1288 $ get-with-headers.py localhost:$HGPORT1 'rcoll/?style=raw'
1288 1289 200 Script output follows
1289 1290
1290 1291
1291 1292 /rcoll/a/
1292 1293 /rcoll/b/
1293 1294 /rcoll/c/
1294 1295
1295 1296 $ get-with-headers.py localhost:$HGPORT1 'rcoll/b/d/file/tip/d?style=raw'
1296 1297 200 Script output follows
1297 1298
1298 1299 d
1299 1300
1300 1301 Test intermediate directories
1301 1302
1302 1303 $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/?style=raw'
1303 1304 200 Script output follows
1304 1305
1305 1306
1306 1307 /rcoll/notrepo/e/
1307 1308 /rcoll/notrepo/f/
1308 1309
1309 1310
1310 1311 Test repositories inside intermediate directories
1311 1312
1312 1313 $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/e/file/tip/e?style=raw'
1313 1314 200 Script output follows
1314 1315
1315 1316 e
1316 1317
1317 1318 Test subrepositories inside intermediate directories
1318 1319
1319 1320 $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/f/f2/file/tip/f2?style=raw'
1320 1321 200 Script output follows
1321 1322
1322 1323 f2
1323 1324
1324 1325 Test [paths] '*' in a repo root
1325 1326
1326 1327 $ hg id http://localhost:$HGPORT1/astar
1327 1328 8580ff50825a
1328 1329
1329 1330 $ killdaemons.py
1330 1331 $ cat > paths.conf <<EOF
1331 1332 > [paths]
1332 1333 > t/a = $root/a
1333 1334 > t/b = $root/b
1334 1335 > c = $root/c
1335 1336 > EOF
1336 1337 $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
1337 1338 > -A access-paths.log -E error-paths-5.log
1338 1339 $ cat hg.pid >> $DAEMON_PIDS
1339 1340 $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
1340 1341 200 Script output follows
1341 1342
1342 1343
1343 1344 /t/a/
1344 1345 /t/b/
1345 1346 /c/
1346 1347
1347 1348 $ get-with-headers.py localhost:$HGPORT1 't/?style=raw'
1348 1349 200 Script output follows
1349 1350
1350 1351
1351 1352 /t/a/
1352 1353 /t/b/
1353 1354
1354 1355
1355 1356 Test collapse = True
1356 1357
1357 1358 $ killdaemons.py
1358 1359 $ cat >> paths.conf <<EOF
1359 1360 > [web]
1360 1361 > collapse=true
1361 1362 > EOF
1362 1363 $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
1363 1364 > -A access-paths.log -E error-paths-6.log
1364 1365 $ cat hg.pid >> $DAEMON_PIDS
1365 1366 $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
1366 1367 200 Script output follows
1367 1368
1368 1369
1369 1370 /t/
1370 1371 /c/
1371 1372
1372 1373 $ get-with-headers.py localhost:$HGPORT1 't/?style=raw'
1373 1374 200 Script output follows
1374 1375
1375 1376
1376 1377 /t/a/
1377 1378 /t/b/
1378 1379
1379 1380
1380 1381 test descend = False
1381 1382
1382 1383 $ killdaemons.py
1383 1384 $ cat >> paths.conf <<EOF
1384 1385 > descend=false
1385 1386 > EOF
1386 1387 $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
1387 1388 > -A access-paths.log -E error-paths-7.log
1388 1389 $ cat hg.pid >> $DAEMON_PIDS
1389 1390 $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
1390 1391 200 Script output follows
1391 1392
1392 1393
1393 1394 /c/
1394 1395
1395 1396 $ get-with-headers.py localhost:$HGPORT1 't/?style=raw'
1396 1397 200 Script output follows
1397 1398
1398 1399
1399 1400 /t/a/
1400 1401 /t/b/
1401 1402
1402 1403 $ killdaemons.py
1403 1404 $ cat > paths.conf <<EOF
1404 1405 > [paths]
1405 1406 > nostore = $root/nostore
1406 1407 > inexistent = $root/inexistent
1407 1408 > EOF
1408 1409 $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
1409 1410 > -A access-paths.log -E error-paths-8.log
1410 1411 $ cat hg.pid >> $DAEMON_PIDS
1411 1412
1412 1413 test inexistent and inaccessible repo should be ignored silently
1413 1414
1414 1415 $ get-with-headers.py localhost:$HGPORT1 ''
1415 1416 200 Script output follows
1416 1417
1417 1418 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1418 1419 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
1419 1420 <head>
1420 1421 <link rel="icon" href="/static/hgicon.png" type="image/png" />
1421 1422 <meta name="robots" content="index, nofollow" />
1422 1423 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
1423 1424 <script type="text/javascript" src="/static/mercurial.js"></script>
1424 1425
1425 1426 <title>Mercurial repositories index</title>
1426 1427 </head>
1427 1428 <body>
1428 1429
1429 1430 <div class="container">
1430 1431 <div class="menu">
1431 1432 <a href="https://mercurial-scm.org/">
1432 1433 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
1433 1434 </div>
1434 1435 <div class="main">
1435 1436 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
1436 1437
1437 1438 <table class="bigtable">
1438 1439 <thead>
1439 1440 <tr>
1440 1441 <th><a href="?sort=name">Name</a></th>
1441 1442 <th><a href="?sort=description">Description</a></th>
1442 1443 <th><a href="?sort=contact">Contact</a></th>
1443 1444 <th><a href="?sort=lastchange">Last modified</a></th>
1444 1445 <th>&nbsp;</th>
1445 1446 <th>&nbsp;</th>
1446 1447 </tr>
1447 1448 </thead>
1448 1449 <tbody class="stripes2">
1449 1450
1450 1451 </tbody>
1451 1452 </table>
1452 1453 </div>
1453 1454 </div>
1454 1455
1455 1456
1456 1457 </body>
1457 1458 </html>
1458 1459
1459 1460
1460 1461 test listening address/port specified by web-conf (issue4699):
1461 1462
1462 1463 $ killdaemons.py
1463 1464 $ cat >> paths.conf <<EOF
1464 1465 > [web]
1465 1466 > address = localhost
1466 1467 > port = $HGPORT1
1467 1468 > EOF
1468 1469 $ hg serve -d --pid-file=hg.pid --web-conf paths.conf \
1469 1470 > -A access-paths.log -E error-paths-9.log
1470 1471 listening at http://*:$HGPORT1/ (bound to *$LOCALIP*:$HGPORT1) (glob) (?)
1471 1472 $ cat hg.pid >> $DAEMON_PIDS
1472 1473 $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
1473 1474 200 Script output follows
1474 1475
1475 1476
1476 1477
1477 1478 test --port option overrides web.port:
1478 1479
1479 1480 $ killdaemons.py
1480 1481 $ hg serve -p $HGPORT2 -d -v --pid-file=hg.pid --web-conf paths.conf \
1481 1482 > -A access-paths.log -E error-paths-10.log
1482 1483 listening at http://*:$HGPORT2/ (bound to *$LOCALIP*:$HGPORT2) (glob) (?)
1483 1484 $ cat hg.pid >> $DAEMON_PIDS
1484 1485 $ get-with-headers.py localhost:$HGPORT2 '?style=raw'
1485 1486 200 Script output follows
1486 1487
1487 1488
1488 1489
1489 1490
1490 1491 $ killdaemons.py
1491 1492 $ cat > collections.conf <<EOF
1492 1493 > [collections]
1493 1494 > $root=$root
1494 1495 > EOF
1495 1496 $ hg serve --config web.baseurl=http://hg.example.com:8080/ -p $HGPORT2 -d \
1496 1497 > --pid-file=hg.pid --webdir-conf collections.conf \
1497 1498 > -A access-collections.log -E error-collections.log
1498 1499 $ cat hg.pid >> $DAEMON_PIDS
1499 1500
1500 1501 collections: should succeed
1501 1502
1502 1503 $ get-with-headers.py localhost:$HGPORT2 '?style=raw'
1503 1504 200 Script output follows
1504 1505
1505 1506
1506 1507 /a/
1507 1508 /a/.hg/patches/
1508 1509 /b/
1509 1510 /c/
1510 1511 /notrepo/e/
1511 1512 /notrepo/f/
1512 1513
1513 1514 $ get-with-headers.py localhost:$HGPORT2 'a/file/tip/a?style=raw'
1514 1515 200 Script output follows
1515 1516
1516 1517 a
1517 1518 $ get-with-headers.py localhost:$HGPORT2 'b/file/tip/b?style=raw'
1518 1519 200 Script output follows
1519 1520
1520 1521 b
1521 1522 $ get-with-headers.py localhost:$HGPORT2 'c/file/tip/c?style=raw'
1522 1523 200 Script output follows
1523 1524
1524 1525 c
1525 1526
1526 1527 atom-log with basedir /
1527 1528
1528 1529 $ get-with-headers.py localhost:$HGPORT2 'a/atom-log' | grep '<link'
1529 1530 <link rel="self" href="http://hg.example.com:8080/a/atom-log"/>
1530 1531 <link rel="alternate" href="http://hg.example.com:8080/a/"/>
1531 1532 <link href="http://hg.example.com:8080/a/rev/8580ff50825a"/>
1532 1533
1533 1534 rss-log with basedir /
1534 1535
1535 1536 $ get-with-headers.py localhost:$HGPORT2 'a/rss-log' | grep '<guid'
1536 1537 <guid isPermaLink="true">http://hg.example.com:8080/a/rev/8580ff50825a</guid>
1537 1538 $ killdaemons.py
1538 1539 $ hg serve --config web.baseurl=http://hg.example.com:8080/foo/ -p $HGPORT2 -d \
1539 1540 > --pid-file=hg.pid --webdir-conf collections.conf \
1540 1541 > -A access-collections-2.log -E error-collections-2.log
1541 1542 $ cat hg.pid >> $DAEMON_PIDS
1542 1543
1543 1544 atom-log with basedir /foo/
1544 1545
1545 1546 $ get-with-headers.py localhost:$HGPORT2 'a/atom-log' | grep '<link'
1546 1547 <link rel="self" href="http://hg.example.com:8080/foo/a/atom-log"/>
1547 1548 <link rel="alternate" href="http://hg.example.com:8080/foo/a/"/>
1548 1549 <link href="http://hg.example.com:8080/foo/a/rev/8580ff50825a"/>
1549 1550
1550 1551 rss-log with basedir /foo/
1551 1552
1552 1553 $ get-with-headers.py localhost:$HGPORT2 'a/rss-log' | grep '<guid'
1553 1554 <guid isPermaLink="true">http://hg.example.com:8080/foo/a/rev/8580ff50825a</guid>
1554 1555
1555 1556 Path refreshing works as expected
1556 1557
1557 1558 $ killdaemons.py
1558 1559 $ mkdir $root/refreshtest
1559 1560 $ hg init $root/refreshtest/a
1560 1561 $ cat > paths.conf << EOF
1561 1562 > [paths]
1562 1563 > / = $root/refreshtest/*
1563 1564 > EOF
1564 1565 $ hg serve -p $HGPORT1 -d --pid-file hg.pid --webdir-conf paths.conf
1565 1566 $ cat hg.pid >> $DAEMON_PIDS
1566 1567
1567 1568 $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
1568 1569 200 Script output follows
1569 1570
1570 1571
1571 1572 /a/
1572 1573
1573 1574
1574 1575 By default refreshing occurs every 20s and a new repo won't be listed
1575 1576 immediately.
1576 1577
1577 1578 $ hg init $root/refreshtest/b
1578 1579 $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
1579 1580 200 Script output follows
1580 1581
1581 1582
1582 1583 /a/
1583 1584
1584 1585
1585 1586 Restart the server with no refresh interval. New repo should appear
1586 1587 immediately.
1587 1588
1588 1589 $ killdaemons.py
1589 1590 $ cat > paths.conf << EOF
1590 1591 > [web]
1591 1592 > refreshinterval = -1
1592 1593 > [paths]
1593 1594 > / = $root/refreshtest/*
1594 1595 > EOF
1595 1596 $ hg serve -p $HGPORT1 -d --pid-file hg.pid --webdir-conf paths.conf
1596 1597 $ cat hg.pid >> $DAEMON_PIDS
1597 1598
1598 1599 $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
1599 1600 200 Script output follows
1600 1601
1601 1602
1602 1603 /a/
1603 1604 /b/
1604 1605
1605 1606
1606 1607 $ hg init $root/refreshtest/c
1607 1608 $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
1608 1609 200 Script output follows
1609 1610
1610 1611
1611 1612 /a/
1612 1613 /b/
1613 1614 /c/
1614 1615
1615 1616 $ killdaemons.py
1616 1617 $ cat > paths.conf << EOF
1617 1618 > [paths]
1618 1619 > /dir1/a_repo = $root/a
1619 1620 > /dir1/a_repo/b_repo = $root/b
1620 1621 > /dir1/dir2/index = $root/b
1621 1622 > EOF
1622 1623 $ hg serve -p $HGPORT1 -d --pid-file hg.pid --webdir-conf paths.conf
1623 1624 $ cat hg.pid >> $DAEMON_PIDS
1624 1625
1625 1626 $ echo 'index file' > $root/a/index
1626 1627 $ hg --cwd $root/a ci -Am 'add index file'
1627 1628 adding index
1628 1629
1629 1630 $ get-with-headers.py localhost:$HGPORT1 '' | grep 'a_repo'
1630 1631 <td><a href="/dir1/a_repo/">dir1/a_repo</a></td>
1631 1632 <a href="/dir1/a_repo/atom-log" title="subscribe to repository atom feed">
1632 1633 <td><a href="/dir1/a_repo/b_repo/">dir1/a_repo/b_repo</a></td>
1633 1634 <a href="/dir1/a_repo/b_repo/atom-log" title="subscribe to repository atom feed">
1634 1635
1635 1636 $ get-with-headers.py localhost:$HGPORT1 'index' | grep 'a_repo'
1636 1637 <td><a href="/dir1/a_repo/">dir1/a_repo</a></td>
1637 1638 <a href="/dir1/a_repo/atom-log" title="subscribe to repository atom feed">
1638 1639 <td><a href="/dir1/a_repo/b_repo/">dir1/a_repo/b_repo</a></td>
1639 1640 <a href="/dir1/a_repo/b_repo/atom-log" title="subscribe to repository atom feed">
1640 1641
1641 1642 $ get-with-headers.py localhost:$HGPORT1 'dir1' | grep 'a_repo'
1642 1643 <td><a href="/dir1/a_repo/">a_repo</a></td>
1643 1644 <a href="/dir1/a_repo/atom-log" title="subscribe to repository atom feed">
1644 1645 <td><a href="/dir1/a_repo/b_repo/">a_repo/b_repo</a></td>
1645 1646 <a href="/dir1/a_repo/b_repo/atom-log" title="subscribe to repository atom feed">
1646 1647
1647 1648 $ get-with-headers.py localhost:$HGPORT1 'dir1/index' | grep 'a_repo'
1648 1649 <td><a href="/dir1/a_repo/">a_repo</a></td>
1649 1650 <a href="/dir1/a_repo/atom-log" title="subscribe to repository atom feed">
1650 1651 <td><a href="/dir1/a_repo/b_repo/">a_repo/b_repo</a></td>
1651 1652 <a href="/dir1/a_repo/b_repo/atom-log" title="subscribe to repository atom feed">
1652 1653
1653 1654 $ get-with-headers.py localhost:$HGPORT1 'dir1/a_repo' | grep 'a_repo'
1654 1655 <link rel="icon" href="/dir1/a_repo/static/hgicon.png" type="image/png" />
1655 1656 <link rel="stylesheet" href="/dir1/a_repo/static/style-paper.css" type="text/css" />
1656 1657 <script type="text/javascript" src="/dir1/a_repo/static/mercurial.js"></script>
1657 1658 <title>dir1/a_repo: log</title>
1658 1659 href="/dir1/a_repo/atom-log" title="Atom feed for dir1/a_repo" />
1659 1660 href="/dir1/a_repo/rss-log" title="RSS feed for dir1/a_repo" />
1660 1661 <img src="/dir1/a_repo/static/hglogo.png" alt="mercurial" /></a>
1661 1662 <li><a href="/dir1/a_repo/graph/tip">graph</a></li>
1662 1663 <li><a href="/dir1/a_repo/tags">tags</a></li>
1663 1664 <li><a href="/dir1/a_repo/bookmarks">bookmarks</a></li>
1664 1665 <li><a href="/dir1/a_repo/branches">branches</a></li>
1665 1666 <li><a href="/dir1/a_repo/rev/tip">changeset</a></li>
1666 1667 <li><a href="/dir1/a_repo/file/tip">browse</a></li>
1667 1668 <li><a href="/dir1/a_repo/help">help</a></li>
1668 1669 <a href="/dir1/a_repo/atom-log" title="subscribe to atom feed">
1669 1670 <img class="atom-logo" src="/dir1/a_repo/static/feed-icon-14x14.png" alt="atom feed" />
1670 1671 <h2 class="breadcrumb"><a href="/">Mercurial</a> &gt; <a href="/dir1">dir1</a> &gt; <a href="/dir1/a_repo">a_repo</a> </h2>
1671 1672 <form class="search" action="/dir1/a_repo/log">
1672 1673 number or hash, or <a href="/dir1/a_repo/help/revsets">revset expression</a>.</div>
1673 1674 <a href="/dir1/a_repo/shortlog/tip?revcount=30">less</a>
1674 1675 <a href="/dir1/a_repo/shortlog/tip?revcount=120">more</a>
1675 1676 | rev 1: <a href="/dir1/a_repo/shortlog/8580ff50825a">(0)</a> <a href="/dir1/a_repo/shortlog/tip">tip</a>
1676 1677 <a href="/dir1/a_repo/rev/71a89161f014">add index file</a>
1677 1678 <a href="/dir1/a_repo/rev/8580ff50825a">a</a>
1678 1679 <a href="/dir1/a_repo/shortlog/tip?revcount=30">less</a>
1679 1680 <a href="/dir1/a_repo/shortlog/tip?revcount=120">more</a>
1680 1681 | rev 1: <a href="/dir1/a_repo/shortlog/8580ff50825a">(0)</a> <a href="/dir1/a_repo/shortlog/tip">tip</a>
1681 1682 '/dir1/a_repo/shortlog/%next%',
1682 1683
1683 1684 $ get-with-headers.py localhost:$HGPORT1 'dir1/a_repo/index' | grep 'a_repo'
1684 1685 <h2 class="breadcrumb"><a href="/">Mercurial</a> &gt; <a href="/dir1">dir1</a> &gt; <a href="/dir1/a_repo">a_repo</a> </h2>
1685 1686 <td><a href="/dir1/a_repo/b_repo/">b_repo</a></td>
1686 1687 <a href="/dir1/a_repo/b_repo/atom-log" title="subscribe to repository atom feed">
1687 1688
1688 1689 Files named 'index' are not blocked
1689 1690
1690 1691 $ get-with-headers.py localhost:$HGPORT1 'dir1/a_repo/raw-file/tip/index'
1691 1692 200 Script output follows
1692 1693
1693 1694 index file
1694 1695
1695 1696 Repos named 'index' take precedence over the index file
1696 1697
1697 1698 $ get-with-headers.py localhost:$HGPORT1 'dir1/dir2/index' | grep 'index'
1698 1699 <link rel="icon" href="/dir1/dir2/index/static/hgicon.png" type="image/png" />
1699 1700 <meta name="robots" content="index, nofollow" />
1700 1701 <link rel="stylesheet" href="/dir1/dir2/index/static/style-paper.css" type="text/css" />
1701 1702 <script type="text/javascript" src="/dir1/dir2/index/static/mercurial.js"></script>
1702 1703 <title>dir1/dir2/index: log</title>
1703 1704 href="/dir1/dir2/index/atom-log" title="Atom feed for dir1/dir2/index" />
1704 1705 href="/dir1/dir2/index/rss-log" title="RSS feed for dir1/dir2/index" />
1705 1706 <img src="/dir1/dir2/index/static/hglogo.png" alt="mercurial" /></a>
1706 1707 <li><a href="/dir1/dir2/index/graph/tip">graph</a></li>
1707 1708 <li><a href="/dir1/dir2/index/tags">tags</a></li>
1708 1709 <li><a href="/dir1/dir2/index/bookmarks">bookmarks</a></li>
1709 1710 <li><a href="/dir1/dir2/index/branches">branches</a></li>
1710 1711 <li><a href="/dir1/dir2/index/rev/tip">changeset</a></li>
1711 1712 <li><a href="/dir1/dir2/index/file/tip">browse</a></li>
1712 1713 <li><a href="/dir1/dir2/index/help">help</a></li>
1713 1714 <a href="/dir1/dir2/index/atom-log" title="subscribe to atom feed">
1714 1715 <img class="atom-logo" src="/dir1/dir2/index/static/feed-icon-14x14.png" alt="atom feed" />
1715 1716 <h2 class="breadcrumb"><a href="/">Mercurial</a> &gt; <a href="/dir1">dir1</a> &gt; <a href="/dir1/dir2">dir2</a> &gt; <a href="/dir1/dir2/index">index</a> </h2>
1716 1717 <form class="search" action="/dir1/dir2/index/log">
1717 1718 number or hash, or <a href="/dir1/dir2/index/help/revsets">revset expression</a>.</div>
1718 1719 <a href="/dir1/dir2/index/shortlog/tip?revcount=30">less</a>
1719 1720 <a href="/dir1/dir2/index/shortlog/tip?revcount=120">more</a>
1720 1721 | rev 0: <a href="/dir1/dir2/index/shortlog/39505516671b">(0)</a> <a href="/dir1/dir2/index/shortlog/tip">tip</a>
1721 1722 <a href="/dir1/dir2/index/rev/39505516671b">b</a>
1722 1723 <a href="/dir1/dir2/index/shortlog/tip?revcount=30">less</a>
1723 1724 <a href="/dir1/dir2/index/shortlog/tip?revcount=120">more</a>
1724 1725 | rev 0: <a href="/dir1/dir2/index/shortlog/39505516671b">(0)</a> <a href="/dir1/dir2/index/shortlog/tip">tip</a>
1725 1726 '/dir1/dir2/index/shortlog/%next%',
1726 1727
1727 1728 $ killdaemons.py
1728 1729
1729 1730 $ cat > paths.conf << EOF
1730 1731 > [paths]
1731 1732 > / = $root/a
1732 1733 > EOF
1733 1734 $ hg serve -p $HGPORT1 -d --pid-file hg.pid --webdir-conf paths.conf
1734 1735 $ cat hg.pid >> $DAEMON_PIDS
1735 1736
1736 1737 $ hg id http://localhost:$HGPORT1
1737 1738 71a89161f014
1738 1739
1739 1740 $ get-with-headers.py localhost:$HGPORT1 '' | grep 'index'
1740 1741 <meta name="robots" content="index, nofollow" />
1741 1742 <a href="/rev/71a89161f014">add index file</a>
1742 1743
1743 1744 $ killdaemons.py
1744 1745
1745 1746 paths errors 1
1746 1747
1747 1748 $ cat error-paths-1.log
1748 1749
1749 1750 paths errors 2
1750 1751
1751 1752 $ cat error-paths-2.log
1752 1753
1753 1754 paths errors 3
1754 1755
1755 1756 $ cat error-paths-3.log
1756 1757
1757 1758 paths errors 4
1758 1759
1759 1760 $ cat error-paths-4.log
1760 1761
1761 1762 paths errors 5
1762 1763
1763 1764 $ cat error-paths-5.log
1764 1765
1765 1766 paths errors 6
1766 1767
1767 1768 $ cat error-paths-6.log
1768 1769
1769 1770 paths errors 7
1770 1771
1771 1772 $ cat error-paths-7.log
1772 1773
1773 1774 paths errors 8
1774 1775
1775 1776 $ cat error-paths-8.log
1776 1777
1777 1778 paths errors 9
1778 1779
1779 1780 $ cat error-paths-9.log
1780 1781
1781 1782 paths errors 10
1782 1783
1783 1784 $ cat error-paths-10.log
1784 1785
1785 1786 collections errors
1786 1787
1787 1788 $ cat error-collections.log
1788 1789
1789 1790 collections errors 2
1790 1791
1791 1792 $ cat error-collections-2.log
General Comments 0
You need to be logged in to leave comments. Login now