##// END OF EJS Templates
hgwebdir: allow a repository to be hosted at "/"...
Matt Harbison -
r32004:bd3cb917 default
parent child Browse files
Show More
@@ -1,483 +1,483 b''
1 1 # hgweb/hgweb_mod.py - Web interface for a repository.
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 contextlib
12 12 import os
13 13
14 14 from .common import (
15 15 ErrorResponse,
16 16 HTTP_BAD_REQUEST,
17 17 HTTP_NOT_FOUND,
18 18 HTTP_NOT_MODIFIED,
19 19 HTTP_OK,
20 20 HTTP_SERVER_ERROR,
21 21 caching,
22 22 cspvalues,
23 23 permhooks,
24 24 )
25 25 from .request import wsgirequest
26 26
27 27 from .. import (
28 28 encoding,
29 29 error,
30 30 hg,
31 31 hook,
32 32 profiling,
33 33 repoview,
34 34 templatefilters,
35 35 templater,
36 36 ui as uimod,
37 37 util,
38 38 )
39 39
40 40 from . import (
41 41 protocol,
42 42 webcommands,
43 43 webutil,
44 44 wsgicgi,
45 45 )
46 46
47 47 perms = {
48 48 'changegroup': 'pull',
49 49 'changegroupsubset': 'pull',
50 50 'getbundle': 'pull',
51 51 'stream_out': 'pull',
52 52 'listkeys': 'pull',
53 53 'unbundle': 'push',
54 54 'pushkey': 'push',
55 55 }
56 56
57 57 archivespecs = util.sortdict((
58 58 ('zip', ('application/zip', 'zip', '.zip', None)),
59 59 ('gz', ('application/x-gzip', 'tgz', '.tar.gz', None)),
60 60 ('bz2', ('application/x-bzip2', 'tbz2', '.tar.bz2', None)),
61 61 ))
62 62
63 63 def makebreadcrumb(url, prefix=''):
64 64 '''Return a 'URL breadcrumb' list
65 65
66 66 A 'URL breadcrumb' is a list of URL-name pairs,
67 67 corresponding to each of the path items on a URL.
68 68 This can be used to create path navigation entries.
69 69 '''
70 70 if url.endswith('/'):
71 71 url = url[:-1]
72 72 if prefix:
73 73 url = '/' + prefix + url
74 74 relpath = url
75 75 if relpath.startswith('/'):
76 76 relpath = relpath[1:]
77 77
78 78 breadcrumb = []
79 79 urlel = url
80 80 pathitems = [''] + relpath.split('/')
81 81 for pathel in reversed(pathitems):
82 82 if not pathel or not urlel:
83 83 break
84 84 breadcrumb.append({'url': urlel, 'name': pathel})
85 85 urlel = os.path.dirname(urlel)
86 86 return reversed(breadcrumb)
87 87
88 88 class requestcontext(object):
89 89 """Holds state/context for an individual request.
90 90
91 91 Servers can be multi-threaded. Holding state on the WSGI application
92 92 is prone to race conditions. Instances of this class exist to hold
93 93 mutable and race-free state for requests.
94 94 """
95 95 def __init__(self, app, repo):
96 96 self.repo = repo
97 97 self.reponame = app.reponame
98 98
99 99 self.archivespecs = archivespecs
100 100
101 101 self.maxchanges = self.configint('web', 'maxchanges', 10)
102 102 self.stripecount = self.configint('web', 'stripes', 1)
103 103 self.maxshortchanges = self.configint('web', 'maxshortchanges', 60)
104 104 self.maxfiles = self.configint('web', 'maxfiles', 10)
105 105 self.allowpull = self.configbool('web', 'allowpull', True)
106 106
107 107 # we use untrusted=False to prevent a repo owner from using
108 108 # web.templates in .hg/hgrc to get access to any file readable
109 109 # by the user running the CGI script
110 110 self.templatepath = self.config('web', 'templates', untrusted=False)
111 111
112 112 # This object is more expensive to build than simple config values.
113 113 # It is shared across requests. The app will replace the object
114 114 # if it is updated. Since this is a reference and nothing should
115 115 # modify the underlying object, it should be constant for the lifetime
116 116 # of the request.
117 117 self.websubtable = app.websubtable
118 118
119 119 self.csp, self.nonce = cspvalues(self.repo.ui)
120 120
121 121 # Trust the settings from the .hg/hgrc files by default.
122 122 def config(self, section, name, default=None, untrusted=True):
123 123 return self.repo.ui.config(section, name, default,
124 124 untrusted=untrusted)
125 125
126 126 def configbool(self, section, name, default=False, untrusted=True):
127 127 return self.repo.ui.configbool(section, name, default,
128 128 untrusted=untrusted)
129 129
130 130 def configint(self, section, name, default=None, untrusted=True):
131 131 return self.repo.ui.configint(section, name, default,
132 132 untrusted=untrusted)
133 133
134 134 def configlist(self, section, name, default=None, untrusted=True):
135 135 return self.repo.ui.configlist(section, name, default,
136 136 untrusted=untrusted)
137 137
138 138 def archivelist(self, nodeid):
139 139 allowed = self.configlist('web', 'allow_archive')
140 140 for typ, spec in self.archivespecs.iteritems():
141 141 if typ in allowed or self.configbool('web', 'allow%s' % typ):
142 142 yield {'type': typ, 'extension': spec[2], 'node': nodeid}
143 143
144 144 def templater(self, req):
145 145 # determine scheme, port and server name
146 146 # this is needed to create absolute urls
147 147
148 148 proto = req.env.get('wsgi.url_scheme')
149 149 if proto == 'https':
150 150 proto = 'https'
151 151 default_port = '443'
152 152 else:
153 153 proto = 'http'
154 154 default_port = '80'
155 155
156 156 port = req.env['SERVER_PORT']
157 157 port = port != default_port and (':' + port) or ''
158 158 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
159 159 logourl = self.config('web', 'logourl', 'https://mercurial-scm.org/')
160 160 logoimg = self.config('web', 'logoimg', 'hglogo.png')
161 161 staticurl = self.config('web', 'staticurl') or req.url + 'static/'
162 162 if not staticurl.endswith('/'):
163 163 staticurl += '/'
164 164
165 165 # some functions for the templater
166 166
167 167 def motd(**map):
168 168 yield self.config('web', 'motd', '')
169 169
170 170 # figure out which style to use
171 171
172 172 vars = {}
173 173 styles = (
174 174 req.form.get('style', [None])[0],
175 175 self.config('web', 'style'),
176 176 'paper',
177 177 )
178 178 style, mapfile = templater.stylemap(styles, self.templatepath)
179 179 if style == styles[0]:
180 180 vars['style'] = style
181 181
182 182 start = req.url[-1] == '?' and '&' or '?'
183 183 sessionvars = webutil.sessionvars(vars, start)
184 184
185 185 if not self.reponame:
186 186 self.reponame = (self.config('web', 'name')
187 187 or req.env.get('REPO_NAME')
188 188 or req.url.strip('/') or self.repo.root)
189 189
190 190 def websubfilter(text):
191 191 return templatefilters.websub(text, self.websubtable)
192 192
193 193 # create the templater
194 194
195 195 defaults = {
196 196 'url': req.url,
197 197 'logourl': logourl,
198 198 'logoimg': logoimg,
199 199 'staticurl': staticurl,
200 200 'urlbase': urlbase,
201 201 'repo': self.reponame,
202 202 'encoding': encoding.encoding,
203 203 'motd': motd,
204 204 'sessionvars': sessionvars,
205 205 'pathdef': makebreadcrumb(req.url),
206 206 'style': style,
207 207 'nonce': self.nonce,
208 208 }
209 209 tmpl = templater.templater.frommapfile(mapfile,
210 210 filters={'websub': websubfilter},
211 211 defaults=defaults)
212 212 return tmpl
213 213
214 214
215 215 class hgweb(object):
216 216 """HTTP server for individual repositories.
217 217
218 218 Instances of this class serve HTTP responses for a particular
219 219 repository.
220 220
221 221 Instances are typically used as WSGI applications.
222 222
223 223 Some servers are multi-threaded. On these servers, there may
224 224 be multiple active threads inside __call__.
225 225 """
226 226 def __init__(self, repo, name=None, baseui=None):
227 227 if isinstance(repo, str):
228 228 if baseui:
229 229 u = baseui.copy()
230 230 else:
231 231 u = uimod.ui.load()
232 232 r = hg.repository(u, repo)
233 233 else:
234 234 # we trust caller to give us a private copy
235 235 r = repo
236 236
237 237 r.ui.setconfig('ui', 'report_untrusted', 'off', 'hgweb')
238 238 r.baseui.setconfig('ui', 'report_untrusted', 'off', 'hgweb')
239 239 r.ui.setconfig('ui', 'nontty', 'true', 'hgweb')
240 240 r.baseui.setconfig('ui', 'nontty', 'true', 'hgweb')
241 241 # resolve file patterns relative to repo root
242 242 r.ui.setconfig('ui', 'forcecwd', r.root, 'hgweb')
243 243 r.baseui.setconfig('ui', 'forcecwd', r.root, 'hgweb')
244 244 # displaying bundling progress bar while serving feel wrong and may
245 245 # break some wsgi implementation.
246 246 r.ui.setconfig('progress', 'disable', 'true', 'hgweb')
247 247 r.baseui.setconfig('progress', 'disable', 'true', 'hgweb')
248 248 self._repos = [hg.cachedlocalrepo(self._webifyrepo(r))]
249 249 self._lastrepo = self._repos[0]
250 250 hook.redirect(True)
251 251 self.reponame = name
252 252
253 253 def _webifyrepo(self, repo):
254 254 repo = getwebview(repo)
255 255 self.websubtable = webutil.getwebsubs(repo)
256 256 return repo
257 257
258 258 @contextlib.contextmanager
259 259 def _obtainrepo(self):
260 260 """Obtain a repo unique to the caller.
261 261
262 262 Internally we maintain a stack of cachedlocalrepo instances
263 263 to be handed out. If one is available, we pop it and return it,
264 264 ensuring it is up to date in the process. If one is not available,
265 265 we clone the most recently used repo instance and return it.
266 266
267 267 It is currently possible for the stack to grow without bounds
268 268 if the server allows infinite threads. However, servers should
269 269 have a thread limit, thus establishing our limit.
270 270 """
271 271 if self._repos:
272 272 cached = self._repos.pop()
273 273 r, created = cached.fetch()
274 274 else:
275 275 cached = self._lastrepo.copy()
276 276 r, created = cached.fetch()
277 277 if created:
278 278 r = self._webifyrepo(r)
279 279
280 280 self._lastrepo = cached
281 281 self.mtime = cached.mtime
282 282 try:
283 283 yield r
284 284 finally:
285 285 self._repos.append(cached)
286 286
287 287 def run(self):
288 288 """Start a server from CGI environment.
289 289
290 290 Modern servers should be using WSGI and should avoid this
291 291 method, if possible.
292 292 """
293 293 if not encoding.environ.get('GATEWAY_INTERFACE',
294 294 '').startswith("CGI/1."):
295 295 raise RuntimeError("This function is only intended to be "
296 296 "called while running as a CGI script.")
297 297 wsgicgi.launch(self)
298 298
299 299 def __call__(self, env, respond):
300 300 """Run the WSGI application.
301 301
302 302 This may be called by multiple threads.
303 303 """
304 304 req = wsgirequest(env, respond)
305 305 return self.run_wsgi(req)
306 306
307 307 def run_wsgi(self, req):
308 308 """Internal method to run the WSGI application.
309 309
310 310 This is typically only called by Mercurial. External consumers
311 311 should be using instances of this class as the WSGI application.
312 312 """
313 313 with self._obtainrepo() as repo:
314 314 with profiling.maybeprofile(repo.ui):
315 315 for r in self._runwsgi(req, repo):
316 316 yield r
317 317
318 318 def _runwsgi(self, req, repo):
319 319 rctx = requestcontext(self, repo)
320 320
321 321 # This state is global across all threads.
322 322 encoding.encoding = rctx.config('web', 'encoding', encoding.encoding)
323 323 rctx.repo.ui.environ = req.env
324 324
325 325 if rctx.csp:
326 326 # hgwebdir may have added CSP header. Since we generate our own,
327 327 # replace it.
328 328 req.headers = [h for h in req.headers
329 329 if h[0] != 'Content-Security-Policy']
330 330 req.headers.append(('Content-Security-Policy', rctx.csp))
331 331
332 332 # work with CGI variables to create coherent structure
333 333 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
334 334
335 335 req.url = req.env['SCRIPT_NAME']
336 336 if not req.url.endswith('/'):
337 337 req.url += '/'
338 if 'REPO_NAME' in req.env:
338 if req.env.get('REPO_NAME'):
339 339 req.url += req.env['REPO_NAME'] + '/'
340 340
341 341 if 'PATH_INFO' in req.env:
342 342 parts = req.env['PATH_INFO'].strip('/').split('/')
343 343 repo_parts = req.env.get('REPO_NAME', '').split('/')
344 344 if parts[:len(repo_parts)] == repo_parts:
345 345 parts = parts[len(repo_parts):]
346 346 query = '/'.join(parts)
347 347 else:
348 348 query = req.env['QUERY_STRING'].partition('&')[0]
349 349 query = query.partition(';')[0]
350 350
351 351 # process this if it's a protocol request
352 352 # protocol bits don't need to create any URLs
353 353 # and the clients always use the old URL structure
354 354
355 355 cmd = req.form.get('cmd', [''])[0]
356 356 if protocol.iscmd(cmd):
357 357 try:
358 358 if query:
359 359 raise ErrorResponse(HTTP_NOT_FOUND)
360 360 if cmd in perms:
361 361 self.check_perm(rctx, req, perms[cmd])
362 362 return protocol.call(rctx.repo, req, cmd)
363 363 except ErrorResponse as inst:
364 364 # A client that sends unbundle without 100-continue will
365 365 # break if we respond early.
366 366 if (cmd == 'unbundle' and
367 367 (req.env.get('HTTP_EXPECT',
368 368 '').lower() != '100-continue') or
369 369 req.env.get('X-HgHttp2', '')):
370 370 req.drain()
371 371 else:
372 372 req.headers.append(('Connection', 'Close'))
373 373 req.respond(inst, protocol.HGTYPE,
374 374 body='0\n%s\n' % inst)
375 375 return ''
376 376
377 377 # translate user-visible url structure to internal structure
378 378
379 379 args = query.split('/', 2)
380 380 if 'cmd' not in req.form and args and args[0]:
381 381
382 382 cmd = args.pop(0)
383 383 style = cmd.rfind('-')
384 384 if style != -1:
385 385 req.form['style'] = [cmd[:style]]
386 386 cmd = cmd[style + 1:]
387 387
388 388 # avoid accepting e.g. style parameter as command
389 389 if util.safehasattr(webcommands, cmd):
390 390 req.form['cmd'] = [cmd]
391 391
392 392 if cmd == 'static':
393 393 req.form['file'] = ['/'.join(args)]
394 394 else:
395 395 if args and args[0]:
396 396 node = args.pop(0).replace('%2F', '/')
397 397 req.form['node'] = [node]
398 398 if args:
399 399 req.form['file'] = args
400 400
401 401 ua = req.env.get('HTTP_USER_AGENT', '')
402 402 if cmd == 'rev' and 'mercurial' in ua:
403 403 req.form['style'] = ['raw']
404 404
405 405 if cmd == 'archive':
406 406 fn = req.form['node'][0]
407 407 for type_, spec in rctx.archivespecs.iteritems():
408 408 ext = spec[2]
409 409 if fn.endswith(ext):
410 410 req.form['node'] = [fn[:-len(ext)]]
411 411 req.form['type'] = [type_]
412 412
413 413 # process the web interface request
414 414
415 415 try:
416 416 tmpl = rctx.templater(req)
417 417 ctype = tmpl('mimetype', encoding=encoding.encoding)
418 418 ctype = templater.stringify(ctype)
419 419
420 420 # check read permissions non-static content
421 421 if cmd != 'static':
422 422 self.check_perm(rctx, req, None)
423 423
424 424 if cmd == '':
425 425 req.form['cmd'] = [tmpl.cache['default']]
426 426 cmd = req.form['cmd'][0]
427 427
428 428 # Don't enable caching if using a CSP nonce because then it wouldn't
429 429 # be a nonce.
430 430 if rctx.configbool('web', 'cache', True) and not rctx.nonce:
431 431 caching(self, req) # sets ETag header or raises NOT_MODIFIED
432 432 if cmd not in webcommands.__all__:
433 433 msg = 'no such method: %s' % cmd
434 434 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
435 435 elif cmd == 'file' and 'raw' in req.form.get('style', []):
436 436 rctx.ctype = ctype
437 437 content = webcommands.rawfile(rctx, req, tmpl)
438 438 else:
439 439 content = getattr(webcommands, cmd)(rctx, req, tmpl)
440 440 req.respond(HTTP_OK, ctype)
441 441
442 442 return content
443 443
444 444 except (error.LookupError, error.RepoLookupError) as err:
445 445 req.respond(HTTP_NOT_FOUND, ctype)
446 446 msg = str(err)
447 447 if (util.safehasattr(err, 'name') and
448 448 not isinstance(err, error.ManifestLookupError)):
449 449 msg = 'revision not found: %s' % err.name
450 450 return tmpl('error', error=msg)
451 451 except (error.RepoError, error.RevlogError) as inst:
452 452 req.respond(HTTP_SERVER_ERROR, ctype)
453 453 return tmpl('error', error=str(inst))
454 454 except ErrorResponse as inst:
455 455 req.respond(inst, ctype)
456 456 if inst.code == HTTP_NOT_MODIFIED:
457 457 # Not allowed to return a body on a 304
458 458 return ['']
459 459 return tmpl('error', error=str(inst))
460 460
461 461 def check_perm(self, rctx, req, op):
462 462 for permhook in permhooks:
463 463 permhook(rctx, req, op)
464 464
465 465 def getwebview(repo):
466 466 """The 'web.view' config controls changeset filter to hgweb. Possible
467 467 values are ``served``, ``visible`` and ``all``. Default is ``served``.
468 468 The ``served`` filter only shows changesets that can be pulled from the
469 469 hgweb instance. The``visible`` filter includes secret changesets but
470 470 still excludes "hidden" one.
471 471
472 472 See the repoview module for details.
473 473
474 474 The option has been around undocumented since Mercurial 2.5, but no
475 475 user ever asked about it. So we better keep it undocumented for now."""
476 476 viewconfig = repo.ui.config('web', 'view', 'served',
477 477 untrusted=True)
478 478 if viewconfig == 'all':
479 479 return repo.unfiltered()
480 480 elif viewconfig in repoview.filtertable:
481 481 return repo.filtered(viewconfig)
482 482 else:
483 483 return repo.filtered('served')
@@ -1,535 +1,539 b''
1 1 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
2 2 #
3 3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 4 # Copyright 2005, 2006 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 os
12 12 import re
13 13 import time
14 14
15 15 from ..i18n import _
16 16
17 17 from .common import (
18 18 ErrorResponse,
19 19 HTTP_NOT_FOUND,
20 20 HTTP_OK,
21 21 HTTP_SERVER_ERROR,
22 22 cspvalues,
23 23 get_contact,
24 24 get_mtime,
25 25 ismember,
26 26 paritygen,
27 27 staticfile,
28 28 )
29 29 from .request import wsgirequest
30 30
31 31 from .. import (
32 32 encoding,
33 33 error,
34 34 hg,
35 35 profiling,
36 36 scmutil,
37 37 templater,
38 38 ui as uimod,
39 39 util,
40 40 )
41 41
42 42 from . import (
43 43 hgweb_mod,
44 44 webutil,
45 45 wsgicgi,
46 46 )
47 47
48 48 def cleannames(items):
49 49 return [(util.pconvert(name).strip('/'), path) for name, path in items]
50 50
51 51 def findrepos(paths):
52 52 repos = []
53 53 for prefix, root in cleannames(paths):
54 54 roothead, roottail = os.path.split(root)
55 55 # "foo = /bar/*" or "foo = /bar/**" lets every repo /bar/N in or below
56 56 # /bar/ be served as as foo/N .
57 57 # '*' will not search inside dirs with .hg (except .hg/patches),
58 58 # '**' will search inside dirs with .hg (and thus also find subrepos).
59 59 try:
60 60 recurse = {'*': False, '**': True}[roottail]
61 61 except KeyError:
62 62 repos.append((prefix, root))
63 63 continue
64 64 roothead = os.path.normpath(os.path.abspath(roothead))
65 65 paths = scmutil.walkrepos(roothead, followsym=True, recurse=recurse)
66 66 repos.extend(urlrepos(prefix, roothead, paths))
67 67 return repos
68 68
69 69 def urlrepos(prefix, roothead, paths):
70 70 """yield url paths and filesystem paths from a list of repo paths
71 71
72 72 >>> conv = lambda seq: [(v, util.pconvert(p)) for v,p in seq]
73 73 >>> conv(urlrepos('hg', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
74 74 [('hg/r', '/opt/r'), ('hg/r/r', '/opt/r/r'), ('hg', '/opt')]
75 75 >>> conv(urlrepos('', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
76 76 [('r', '/opt/r'), ('r/r', '/opt/r/r'), ('', '/opt')]
77 77 """
78 78 for path in paths:
79 79 path = os.path.normpath(path)
80 80 yield (prefix + '/' +
81 81 util.pconvert(path[len(roothead):]).lstrip('/')).strip('/'), path
82 82
83 83 def geturlcgivars(baseurl, port):
84 84 """
85 85 Extract CGI variables from baseurl
86 86
87 87 >>> geturlcgivars("http://host.org/base", "80")
88 88 ('host.org', '80', '/base')
89 89 >>> geturlcgivars("http://host.org:8000/base", "80")
90 90 ('host.org', '8000', '/base')
91 91 >>> geturlcgivars('/base', 8000)
92 92 ('', '8000', '/base')
93 93 >>> geturlcgivars("base", '8000')
94 94 ('', '8000', '/base')
95 95 >>> geturlcgivars("http://host", '8000')
96 96 ('host', '8000', '/')
97 97 >>> geturlcgivars("http://host/", '8000')
98 98 ('host', '8000', '/')
99 99 """
100 100 u = util.url(baseurl)
101 101 name = u.host or ''
102 102 if u.port:
103 103 port = u.port
104 104 path = u.path or ""
105 105 if not path.startswith('/'):
106 106 path = '/' + path
107 107
108 108 return name, str(port), path
109 109
110 110 class hgwebdir(object):
111 111 """HTTP server for multiple repositories.
112 112
113 113 Given a configuration, different repositories will be served depending
114 114 on the request path.
115 115
116 116 Instances are typically used as WSGI applications.
117 117 """
118 118 def __init__(self, conf, baseui=None):
119 119 self.conf = conf
120 120 self.baseui = baseui
121 121 self.ui = None
122 122 self.lastrefresh = 0
123 123 self.motd = None
124 124 self.refresh()
125 125
126 126 def refresh(self):
127 127 refreshinterval = 20
128 128 if self.ui:
129 129 refreshinterval = self.ui.configint('web', 'refreshinterval',
130 130 refreshinterval)
131 131
132 132 # refreshinterval <= 0 means to always refresh.
133 133 if (refreshinterval > 0 and
134 134 self.lastrefresh + refreshinterval > time.time()):
135 135 return
136 136
137 137 if self.baseui:
138 138 u = self.baseui.copy()
139 139 else:
140 140 u = uimod.ui.load()
141 141 u.setconfig('ui', 'report_untrusted', 'off', 'hgwebdir')
142 142 u.setconfig('ui', 'nontty', 'true', 'hgwebdir')
143 143 # displaying bundling progress bar while serving feels wrong and may
144 144 # break some wsgi implementations.
145 145 u.setconfig('progress', 'disable', 'true', 'hgweb')
146 146
147 147 if not isinstance(self.conf, (dict, list, tuple)):
148 148 map = {'paths': 'hgweb-paths'}
149 149 if not os.path.exists(self.conf):
150 150 raise error.Abort(_('config file %s not found!') % self.conf)
151 151 u.readconfig(self.conf, remap=map, trust=True)
152 152 paths = []
153 153 for name, ignored in u.configitems('hgweb-paths'):
154 154 for path in u.configlist('hgweb-paths', name):
155 155 paths.append((name, path))
156 156 elif isinstance(self.conf, (list, tuple)):
157 157 paths = self.conf
158 158 elif isinstance(self.conf, dict):
159 159 paths = self.conf.items()
160 160
161 161 repos = findrepos(paths)
162 162 for prefix, root in u.configitems('collections'):
163 163 prefix = util.pconvert(prefix)
164 164 for path in scmutil.walkrepos(root, followsym=True):
165 165 repo = os.path.normpath(path)
166 166 name = util.pconvert(repo)
167 167 if name.startswith(prefix):
168 168 name = name[len(prefix):]
169 169 repos.append((name.lstrip('/'), repo))
170 170
171 171 self.repos = repos
172 172 self.ui = u
173 173 encoding.encoding = self.ui.config('web', 'encoding',
174 174 encoding.encoding)
175 175 self.style = self.ui.config('web', 'style', 'paper')
176 176 self.templatepath = self.ui.config('web', 'templates', None)
177 177 self.stripecount = self.ui.config('web', 'stripes', 1)
178 178 if self.stripecount:
179 179 self.stripecount = int(self.stripecount)
180 180 self._baseurl = self.ui.config('web', 'baseurl')
181 181 prefix = self.ui.config('web', 'prefix', '')
182 182 if prefix.startswith('/'):
183 183 prefix = prefix[1:]
184 184 if prefix.endswith('/'):
185 185 prefix = prefix[:-1]
186 186 self.prefix = prefix
187 187 self.lastrefresh = time.time()
188 188
189 189 def run(self):
190 190 if not encoding.environ.get('GATEWAY_INTERFACE',
191 191 '').startswith("CGI/1."):
192 192 raise RuntimeError("This function is only intended to be "
193 193 "called while running as a CGI script.")
194 194 wsgicgi.launch(self)
195 195
196 196 def __call__(self, env, respond):
197 197 req = wsgirequest(env, respond)
198 198 return self.run_wsgi(req)
199 199
200 200 def read_allowed(self, ui, req):
201 201 """Check allow_read and deny_read config options of a repo's ui object
202 202 to determine user permissions. By default, with neither option set (or
203 203 both empty), allow all users to read the repo. There are two ways a
204 204 user can be denied read access: (1) deny_read is not empty, and the
205 205 user is unauthenticated or deny_read contains user (or *), and (2)
206 206 allow_read is not empty and the user is not in allow_read. Return True
207 207 if user is allowed to read the repo, else return False."""
208 208
209 209 user = req.env.get('REMOTE_USER')
210 210
211 211 deny_read = ui.configlist('web', 'deny_read', untrusted=True)
212 212 if deny_read and (not user or ismember(ui, user, deny_read)):
213 213 return False
214 214
215 215 allow_read = ui.configlist('web', 'allow_read', untrusted=True)
216 216 # by default, allow reading if no allow_read option has been set
217 217 if (not allow_read) or ismember(ui, user, allow_read):
218 218 return True
219 219
220 220 return False
221 221
222 222 def run_wsgi(self, req):
223 223 with profiling.maybeprofile(self.ui):
224 224 for r in self._runwsgi(req):
225 225 yield r
226 226
227 227 def _runwsgi(self, req):
228 228 try:
229 229 self.refresh()
230 230
231 231 csp, nonce = cspvalues(self.ui)
232 232 if csp:
233 233 req.headers.append(('Content-Security-Policy', csp))
234 234
235 235 virtual = req.env.get("PATH_INFO", "").strip('/')
236 236 tmpl = self.templater(req, nonce)
237 237 ctype = tmpl('mimetype', encoding=encoding.encoding)
238 238 ctype = templater.stringify(ctype)
239 239
240 240 # a static file
241 241 if virtual.startswith('static/') or 'static' in req.form:
242 242 if virtual.startswith('static/'):
243 243 fname = virtual[7:]
244 244 else:
245 245 fname = req.form['static'][0]
246 246 static = self.ui.config("web", "static", None,
247 247 untrusted=False)
248 248 if not static:
249 249 tp = self.templatepath or templater.templatepaths()
250 250 if isinstance(tp, str):
251 251 tp = [tp]
252 252 static = [os.path.join(p, 'static') for p in tp]
253 253 staticfile(static, fname, req)
254 254 return []
255 255
256 256 # top-level index
257 257
258 258 repos = dict(self.repos)
259 259
260 if not virtual or (virtual == 'index' and virtual not in repos):
260 if (not virtual or virtual == 'index') and virtual not in repos:
261 261 req.respond(HTTP_OK, ctype)
262 262 return self.makeindex(req, tmpl)
263 263
264 264 # nested indexes and hgwebs
265 265
266 266 if virtual.endswith('/index') and virtual not in repos:
267 267 subdir = virtual[:-len('index')]
268 268 if any(r.startswith(subdir) for r in repos):
269 269 req.respond(HTTP_OK, ctype)
270 270 return self.makeindex(req, tmpl, subdir)
271 271
272 virtualrepo = virtual
273 while virtualrepo:
272 def _virtualdirs():
273 # Check the full virtual path, each parent, and the root ('')
274 if virtual != '':
275 yield virtual
276
277 for p in util.finddirs(virtual):
278 yield p
279
280 yield ''
281
282 for virtualrepo in _virtualdirs():
274 283 real = repos.get(virtualrepo)
275 284 if real:
276 285 req.env['REPO_NAME'] = virtualrepo
277 286 try:
278 287 # ensure caller gets private copy of ui
279 288 repo = hg.repository(self.ui.copy(), real)
280 289 return hgweb_mod.hgweb(repo).run_wsgi(req)
281 290 except IOError as inst:
282 291 msg = inst.strerror
283 292 raise ErrorResponse(HTTP_SERVER_ERROR, msg)
284 293 except error.RepoError as inst:
285 294 raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
286 295
287 up = virtualrepo.rfind('/')
288 if up < 0:
289 break
290 virtualrepo = virtualrepo[:up]
291
292 296 # browse subdirectories
293 297 subdir = virtual + '/'
294 298 if [r for r in repos if r.startswith(subdir)]:
295 299 req.respond(HTTP_OK, ctype)
296 300 return self.makeindex(req, tmpl, subdir)
297 301
298 302 # prefixes not found
299 303 req.respond(HTTP_NOT_FOUND, ctype)
300 304 return tmpl("notfound", repo=virtual)
301 305
302 306 except ErrorResponse as err:
303 307 req.respond(err, ctype)
304 308 return tmpl('error', error=err.message or '')
305 309 finally:
306 310 tmpl = None
307 311
308 312 def makeindex(self, req, tmpl, subdir=""):
309 313
310 314 def archivelist(ui, nodeid, url):
311 315 allowed = ui.configlist("web", "allow_archive", untrusted=True)
312 316 archives = []
313 317 for typ, spec in hgweb_mod.archivespecs.iteritems():
314 318 if typ in allowed or ui.configbool("web", "allow" + typ,
315 319 untrusted=True):
316 320 archives.append({"type" : typ, "extension": spec[2],
317 321 "node": nodeid, "url": url})
318 322 return archives
319 323
320 324 def rawentries(subdir="", **map):
321 325
322 326 descend = self.ui.configbool('web', 'descend', True)
323 327 collapse = self.ui.configbool('web', 'collapse', False)
324 328 seenrepos = set()
325 329 seendirs = set()
326 330 for name, path in self.repos:
327 331
328 332 if not name.startswith(subdir):
329 333 continue
330 334 name = name[len(subdir):]
331 335 directory = False
332 336
333 337 if '/' in name:
334 338 if not descend:
335 339 continue
336 340
337 341 nameparts = name.split('/')
338 342 rootname = nameparts[0]
339 343
340 344 if not collapse:
341 345 pass
342 346 elif rootname in seendirs:
343 347 continue
344 348 elif rootname in seenrepos:
345 349 pass
346 350 else:
347 351 directory = True
348 352 name = rootname
349 353
350 354 # redefine the path to refer to the directory
351 355 discarded = '/'.join(nameparts[1:])
352 356
353 357 # remove name parts plus accompanying slash
354 358 path = path[:-len(discarded) - 1]
355 359
356 360 try:
357 361 r = hg.repository(self.ui, path)
358 362 directory = False
359 363 except (IOError, error.RepoError):
360 364 pass
361 365
362 366 parts = [name]
363 367 parts.insert(0, '/' + subdir.rstrip('/'))
364 368 if req.env['SCRIPT_NAME']:
365 369 parts.insert(0, req.env['SCRIPT_NAME'])
366 370 url = re.sub(r'/+', '/', '/'.join(parts) + '/')
367 371
368 372 # show either a directory entry or a repository
369 373 if directory:
370 374 # get the directory's time information
371 375 try:
372 376 d = (get_mtime(path), util.makedate()[1])
373 377 except OSError:
374 378 continue
375 379
376 380 # add '/' to the name to make it obvious that
377 381 # the entry is a directory, not a regular repository
378 382 row = {'contact': "",
379 383 'contact_sort': "",
380 384 'name': name + '/',
381 385 'name_sort': name,
382 386 'url': url,
383 387 'description': "",
384 388 'description_sort': "",
385 389 'lastchange': d,
386 390 'lastchange_sort': d[1]-d[0],
387 391 'archives': [],
388 392 'isdirectory': True,
389 393 'labels': [],
390 394 }
391 395
392 396 seendirs.add(name)
393 397 yield row
394 398 continue
395 399
396 400 u = self.ui.copy()
397 401 try:
398 402 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
399 403 except Exception as e:
400 404 u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e))
401 405 continue
402 406 def get(section, name, default=None):
403 407 return u.config(section, name, default, untrusted=True)
404 408
405 409 if u.configbool("web", "hidden", untrusted=True):
406 410 continue
407 411
408 412 if not self.read_allowed(u, req):
409 413 continue
410 414
411 415 # update time with local timezone
412 416 try:
413 417 r = hg.repository(self.ui, path)
414 418 except IOError:
415 419 u.warn(_('error accessing repository at %s\n') % path)
416 420 continue
417 421 except error.RepoError:
418 422 u.warn(_('error accessing repository at %s\n') % path)
419 423 continue
420 424 try:
421 425 d = (get_mtime(r.spath), util.makedate()[1])
422 426 except OSError:
423 427 continue
424 428
425 429 contact = get_contact(get)
426 430 description = get("web", "description", "")
427 431 seenrepos.add(name)
428 432 name = get("web", "name", name)
429 433 row = {'contact': contact or "unknown",
430 434 'contact_sort': contact.upper() or "unknown",
431 435 'name': name,
432 436 'name_sort': name,
433 437 'url': url,
434 438 'description': description or "unknown",
435 439 'description_sort': description.upper() or "unknown",
436 440 'lastchange': d,
437 441 'lastchange_sort': d[1]-d[0],
438 442 'archives': archivelist(u, "tip", url),
439 443 'isdirectory': None,
440 444 'labels': u.configlist('web', 'labels', untrusted=True),
441 445 }
442 446
443 447 yield row
444 448
445 449 sortdefault = None, False
446 450 def entries(sortcolumn="", descending=False, subdir="", **map):
447 451 rows = rawentries(subdir=subdir, **map)
448 452
449 453 if sortcolumn and sortdefault != (sortcolumn, descending):
450 454 sortkey = '%s_sort' % sortcolumn
451 455 rows = sorted(rows, key=lambda x: x[sortkey],
452 456 reverse=descending)
453 457 for row, parity in zip(rows, paritygen(self.stripecount)):
454 458 row['parity'] = parity
455 459 yield row
456 460
457 461 self.refresh()
458 462 sortable = ["name", "description", "contact", "lastchange"]
459 463 sortcolumn, descending = sortdefault
460 464 if 'sort' in req.form:
461 465 sortcolumn = req.form['sort'][0]
462 466 descending = sortcolumn.startswith('-')
463 467 if descending:
464 468 sortcolumn = sortcolumn[1:]
465 469 if sortcolumn not in sortable:
466 470 sortcolumn = ""
467 471
468 472 sort = [("sort_%s" % column,
469 473 "%s%s" % ((not descending and column == sortcolumn)
470 474 and "-" or "", column))
471 475 for column in sortable]
472 476
473 477 self.refresh()
474 478 self.updatereqenv(req.env)
475 479
476 480 return tmpl("index", entries=entries, subdir=subdir,
477 481 pathdef=hgweb_mod.makebreadcrumb('/' + subdir, self.prefix),
478 482 sortcolumn=sortcolumn, descending=descending,
479 483 **dict(sort))
480 484
481 485 def templater(self, req, nonce):
482 486
483 487 def motd(**map):
484 488 if self.motd is not None:
485 489 yield self.motd
486 490 else:
487 491 yield config('web', 'motd', '')
488 492
489 493 def config(section, name, default=None, untrusted=True):
490 494 return self.ui.config(section, name, default, untrusted)
491 495
492 496 self.updatereqenv(req.env)
493 497
494 498 url = req.env.get('SCRIPT_NAME', '')
495 499 if not url.endswith('/'):
496 500 url += '/'
497 501
498 502 vars = {}
499 503 styles = (
500 504 req.form.get('style', [None])[0],
501 505 config('web', 'style'),
502 506 'paper'
503 507 )
504 508 style, mapfile = templater.stylemap(styles, self.templatepath)
505 509 if style == styles[0]:
506 510 vars['style'] = style
507 511
508 512 start = url[-1] == '?' and '&' or '?'
509 513 sessionvars = webutil.sessionvars(vars, start)
510 514 logourl = config('web', 'logourl', 'https://mercurial-scm.org/')
511 515 logoimg = config('web', 'logoimg', 'hglogo.png')
512 516 staticurl = config('web', 'staticurl') or url + 'static/'
513 517 if not staticurl.endswith('/'):
514 518 staticurl += '/'
515 519
516 520 defaults = {
517 521 "encoding": encoding.encoding,
518 522 "motd": motd,
519 523 "url": url,
520 524 "logourl": logourl,
521 525 "logoimg": logoimg,
522 526 "staticurl": staticurl,
523 527 "sessionvars": sessionvars,
524 528 "style": style,
525 529 "nonce": nonce,
526 530 }
527 531 tmpl = templater.templater.frommapfile(mapfile, defaults=defaults)
528 532 return tmpl
529 533
530 534 def updatereqenv(self, env):
531 535 if self._baseurl is not None:
532 536 name, port, path = geturlcgivars(self._baseurl, env['SERVER_PORT'])
533 537 env['SERVER_NAME'] = name
534 538 env['SERVER_PORT'] = port
535 539 env['SCRIPT_NAME'] = path
@@ -1,1729 +1,1745 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 create repository without .hg/store
70 70
71 71 $ hg init nostore
72 72 $ rm -R nostore/.hg/store
73 73 $ root=`pwd`
74 74 $ cd ..
75 75
76 76 serve
77 77 $ cat > paths.conf <<EOF
78 78 > [paths]
79 79 > a=$root/a
80 80 > b=$root/b
81 81 > EOF
82 82 $ hg serve -p $HGPORT -d --pid-file=hg.pid --webdir-conf paths.conf \
83 83 > -A access-paths.log -E error-paths-1.log
84 84 $ cat hg.pid >> $DAEMON_PIDS
85 85
86 86 should give a 404 - file does not exist
87 87
88 88 $ get-with-headers.py localhost:$HGPORT 'a/file/tip/bork?style=raw'
89 89 404 Not Found
90 90
91 91
92 92 error: bork@8580ff50825a: not found in manifest
93 93 [1]
94 94
95 95 should succeed
96 96
97 97 $ get-with-headers.py localhost:$HGPORT '?style=raw'
98 98 200 Script output follows
99 99
100 100
101 101 /a/
102 102 /b/
103 103
104 104 $ get-with-headers.py localhost:$HGPORT '?style=json'
105 105 200 Script output follows
106 106
107 107 {
108 108 "entries": [{
109 109 "name": "a",
110 110 "description": "unknown",
111 111 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
112 112 "lastchange": [*, *], (glob)
113 113 "labels": []
114 114 }, {
115 115 "name": "b",
116 116 "description": "unknown",
117 117 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
118 118 "lastchange": [*, *], (glob)
119 119 "labels": []
120 120 }]
121 121 } (no-eol)
122 122
123 123 $ get-with-headers.py localhost:$HGPORT 'a/file/tip/a?style=raw'
124 124 200 Script output follows
125 125
126 126 a
127 127 $ get-with-headers.py localhost:$HGPORT 'b/file/tip/b?style=raw'
128 128 200 Script output follows
129 129
130 130 b
131 131
132 132 should give a 404 - repo is not published
133 133
134 134 $ get-with-headers.py localhost:$HGPORT 'c/file/tip/c?style=raw'
135 135 404 Not Found
136 136
137 137
138 138 error: repository c/file/tip/c not found
139 139 [1]
140 140
141 141 atom-log without basedir
142 142
143 143 $ get-with-headers.py localhost:$HGPORT 'a/atom-log' | grep '<link'
144 144 <link rel="self" href="http://*:$HGPORT/a/atom-log"/> (glob)
145 145 <link rel="alternate" href="http://*:$HGPORT/a/"/> (glob)
146 146 <link href="http://*:$HGPORT/a/rev/8580ff50825a"/> (glob)
147 147
148 148 rss-log without basedir
149 149
150 150 $ get-with-headers.py localhost:$HGPORT 'a/rss-log' | grep '<guid'
151 151 <guid isPermaLink="true">http://*:$HGPORT/a/rev/8580ff50825a</guid> (glob)
152 152 $ cat > paths.conf <<EOF
153 153 > [paths]
154 154 > t/a/=$root/a
155 155 > b=$root/b
156 156 > coll=$root/*
157 157 > rcoll=$root/**
158 158 > star=*
159 159 > starstar=**
160 160 > astar=webdir/a/*
161 161 > EOF
162 162 $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
163 163 > -A access-paths.log -E error-paths-2.log
164 164 $ cat hg.pid >> $DAEMON_PIDS
165 165
166 166 should succeed, slashy names
167 167
168 168 $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
169 169 200 Script output follows
170 170
171 171
172 172 /t/a/
173 173 /b/
174 174 /coll/a/
175 175 /coll/a/.hg/patches/
176 176 /coll/b/
177 177 /coll/c/
178 178 /coll/notrepo/e/
179 179 /coll/notrepo/f/
180 180 /rcoll/a/
181 181 /rcoll/a/.hg/patches/
182 182 /rcoll/b/
183 183 /rcoll/b/d/
184 184 /rcoll/c/
185 185 /rcoll/notrepo/e/
186 186 /rcoll/notrepo/e/e2/
187 187 /rcoll/notrepo/f/
188 188 /rcoll/notrepo/f/f2/
189 189 /star/webdir/a/
190 190 /star/webdir/a/.hg/patches/
191 191 /star/webdir/b/
192 192 /star/webdir/c/
193 193 /star/webdir/notrepo/e/
194 194 /star/webdir/notrepo/f/
195 195 /starstar/webdir/a/
196 196 /starstar/webdir/a/.hg/patches/
197 197 /starstar/webdir/b/
198 198 /starstar/webdir/b/d/
199 199 /starstar/webdir/c/
200 200 /starstar/webdir/notrepo/e/
201 201 /starstar/webdir/notrepo/e/e2/
202 202 /starstar/webdir/notrepo/f/
203 203 /starstar/webdir/notrepo/f/f2/
204 204 /astar/
205 205 /astar/.hg/patches/
206 206
207 207
208 208 $ get-with-headers.py localhost:$HGPORT1 '?style=json'
209 209 200 Script output follows
210 210
211 211 {
212 212 "entries": [{
213 213 "name": "t/a",
214 214 "description": "unknown",
215 215 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
216 216 "lastchange": [*, *], (glob)
217 217 "labels": []
218 218 }, {
219 219 "name": "b",
220 220 "description": "unknown",
221 221 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
222 222 "lastchange": [*, *], (glob)
223 223 "labels": []
224 224 }, {
225 225 "name": "coll/a",
226 226 "description": "unknown",
227 227 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
228 228 "lastchange": [*, *], (glob)
229 229 "labels": []
230 230 }, {
231 231 "name": "coll/a/.hg/patches",
232 232 "description": "unknown",
233 233 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
234 234 "lastchange": [*, *], (glob)
235 235 "labels": []
236 236 }, {
237 237 "name": "coll/b",
238 238 "description": "unknown",
239 239 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
240 240 "lastchange": [*, *], (glob)
241 241 "labels": []
242 242 }, {
243 243 "name": "coll/c",
244 244 "description": "unknown",
245 245 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
246 246 "lastchange": [*, *], (glob)
247 247 "labels": []
248 248 }, {
249 249 "name": "coll/notrepo/e",
250 250 "description": "unknown",
251 251 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
252 252 "lastchange": [*, *], (glob)
253 253 "labels": []
254 254 }, {
255 255 "name": "fancy name for repo f",
256 256 "description": "unknown",
257 257 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
258 258 "lastchange": [*, *], (glob)
259 259 "labels": ["foo", "bar"]
260 260 }, {
261 261 "name": "rcoll/a",
262 262 "description": "unknown",
263 263 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
264 264 "lastchange": [*, *], (glob)
265 265 "labels": []
266 266 }, {
267 267 "name": "rcoll/a/.hg/patches",
268 268 "description": "unknown",
269 269 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
270 270 "lastchange": [*, *], (glob)
271 271 "labels": []
272 272 }, {
273 273 "name": "rcoll/b",
274 274 "description": "unknown",
275 275 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
276 276 "lastchange": [*, *], (glob)
277 277 "labels": []
278 278 }, {
279 279 "name": "rcoll/b/d",
280 280 "description": "unknown",
281 281 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
282 282 "lastchange": [*, *], (glob)
283 283 "labels": []
284 284 }, {
285 285 "name": "rcoll/c",
286 286 "description": "unknown",
287 287 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
288 288 "lastchange": [*, *], (glob)
289 289 "labels": []
290 290 }, {
291 291 "name": "rcoll/notrepo/e",
292 292 "description": "unknown",
293 293 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
294 294 "lastchange": [*, *], (glob)
295 295 "labels": []
296 296 }, {
297 297 "name": "rcoll/notrepo/e/e2",
298 298 "description": "unknown",
299 299 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
300 300 "lastchange": [*, *], (glob)
301 301 "labels": []
302 302 }, {
303 303 "name": "fancy name for repo f",
304 304 "description": "unknown",
305 305 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
306 306 "lastchange": [*, *], (glob)
307 307 "labels": ["foo", "bar"]
308 308 }, {
309 309 "name": "rcoll/notrepo/f/f2",
310 310 "description": "unknown",
311 311 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
312 312 "lastchange": [*, *], (glob)
313 313 "labels": []
314 314 }, {
315 315 "name": "star/webdir/a",
316 316 "description": "unknown",
317 317 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
318 318 "lastchange": [*, *], (glob)
319 319 "labels": []
320 320 }, {
321 321 "name": "star/webdir/a/.hg/patches",
322 322 "description": "unknown",
323 323 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
324 324 "lastchange": [*, *], (glob)
325 325 "labels": []
326 326 }, {
327 327 "name": "star/webdir/b",
328 328 "description": "unknown",
329 329 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
330 330 "lastchange": [*, *], (glob)
331 331 "labels": []
332 332 }, {
333 333 "name": "star/webdir/c",
334 334 "description": "unknown",
335 335 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
336 336 "lastchange": [*, *], (glob)
337 337 "labels": []
338 338 }, {
339 339 "name": "star/webdir/notrepo/e",
340 340 "description": "unknown",
341 341 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
342 342 "lastchange": [*, *], (glob)
343 343 "labels": []
344 344 }, {
345 345 "name": "fancy name for repo f",
346 346 "description": "unknown",
347 347 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
348 348 "lastchange": [*, *], (glob)
349 349 "labels": ["foo", "bar"]
350 350 }, {
351 351 "name": "starstar/webdir/a",
352 352 "description": "unknown",
353 353 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
354 354 "lastchange": [*, *], (glob)
355 355 "labels": []
356 356 }, {
357 357 "name": "starstar/webdir/a/.hg/patches",
358 358 "description": "unknown",
359 359 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
360 360 "lastchange": [*, *], (glob)
361 361 "labels": []
362 362 }, {
363 363 "name": "starstar/webdir/b",
364 364 "description": "unknown",
365 365 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
366 366 "lastchange": [*, *], (glob)
367 367 "labels": []
368 368 }, {
369 369 "name": "starstar/webdir/b/d",
370 370 "description": "unknown",
371 371 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
372 372 "lastchange": [*, *], (glob)
373 373 "labels": []
374 374 }, {
375 375 "name": "starstar/webdir/c",
376 376 "description": "unknown",
377 377 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
378 378 "lastchange": [*, *], (glob)
379 379 "labels": []
380 380 }, {
381 381 "name": "starstar/webdir/notrepo/e",
382 382 "description": "unknown",
383 383 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
384 384 "lastchange": [*, *], (glob)
385 385 "labels": []
386 386 }, {
387 387 "name": "starstar/webdir/notrepo/e/e2",
388 388 "description": "unknown",
389 389 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
390 390 "lastchange": [*, *], (glob)
391 391 "labels": []
392 392 }, {
393 393 "name": "fancy name for repo f",
394 394 "description": "unknown",
395 395 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
396 396 "lastchange": [*, *], (glob)
397 397 "labels": ["foo", "bar"]
398 398 }, {
399 399 "name": "starstar/webdir/notrepo/f/f2",
400 400 "description": "unknown",
401 401 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
402 402 "lastchange": [*, *], (glob)
403 403 "labels": []
404 404 }, {
405 405 "name": "astar",
406 406 "description": "unknown",
407 407 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
408 408 "lastchange": [*, *], (glob)
409 409 "labels": []
410 410 }, {
411 411 "name": "astar/.hg/patches",
412 412 "description": "unknown",
413 413 "contact": "Foo Bar \u003cfoo.bar@example.com\u003e",
414 414 "lastchange": [*, *], (glob)
415 415 "labels": []
416 416 }]
417 417 } (no-eol)
418 418
419 419 $ get-with-headers.py localhost:$HGPORT1 '?style=paper'
420 420 200 Script output follows
421 421
422 422 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
423 423 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
424 424 <head>
425 425 <link rel="icon" href="/static/hgicon.png" type="image/png" />
426 426 <meta name="robots" content="index, nofollow" />
427 427 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
428 428 <script type="text/javascript" src="/static/mercurial.js"></script>
429 429
430 430 <title>Mercurial repositories index</title>
431 431 </head>
432 432 <body>
433 433
434 434 <div class="container">
435 435 <div class="menu">
436 436 <a href="https://mercurial-scm.org/">
437 437 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
438 438 </div>
439 439 <div class="main">
440 440 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
441 441
442 442 <table class="bigtable">
443 443 <thead>
444 444 <tr>
445 445 <th><a href="?sort=name">Name</a></th>
446 446 <th><a href="?sort=description">Description</a></th>
447 447 <th><a href="?sort=contact">Contact</a></th>
448 448 <th><a href="?sort=lastchange">Last modified</a></th>
449 449 <th>&nbsp;</th>
450 450 <th>&nbsp;</th>
451 451 </tr>
452 452 </thead>
453 453 <tbody class="stripes2">
454 454
455 455 <tr>
456 456 <td><a href="/t/a/?style=paper">t/a</a></td>
457 457 <td>unknown</td>
458 458 <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>
459 459 <td class="age">*</td> (glob)
460 460 <td class="indexlinks"></td>
461 461 <td>
462 462 <a href="/t/a/atom-log" title="subscribe to repository atom feed">
463 463 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
464 464 </a>
465 465 </td>
466 466 </tr>
467 467
468 468 <tr>
469 469 <td><a href="/b/?style=paper">b</a></td>
470 470 <td>unknown</td>
471 471 <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>
472 472 <td class="age">*</td> (glob)
473 473 <td class="indexlinks"></td>
474 474 <td>
475 475 <a href="/b/atom-log" title="subscribe to repository atom feed">
476 476 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
477 477 </a>
478 478 </td>
479 479 </tr>
480 480
481 481 <tr>
482 482 <td><a href="/coll/a/?style=paper">coll/a</a></td>
483 483 <td>unknown</td>
484 484 <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>
485 485 <td class="age">*</td> (glob)
486 486 <td class="indexlinks"></td>
487 487 <td>
488 488 <a href="/coll/a/atom-log" title="subscribe to repository atom feed">
489 489 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
490 490 </a>
491 491 </td>
492 492 </tr>
493 493
494 494 <tr>
495 495 <td><a href="/coll/a/.hg/patches/?style=paper">coll/a/.hg/patches</a></td>
496 496 <td>unknown</td>
497 497 <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>
498 498 <td class="age">*</td> (glob)
499 499 <td class="indexlinks"></td>
500 500 <td>
501 501 <a href="/coll/a/.hg/patches/atom-log" title="subscribe to repository atom feed">
502 502 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
503 503 </a>
504 504 </td>
505 505 </tr>
506 506
507 507 <tr>
508 508 <td><a href="/coll/b/?style=paper">coll/b</a></td>
509 509 <td>unknown</td>
510 510 <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>
511 511 <td class="age">*</td> (glob)
512 512 <td class="indexlinks"></td>
513 513 <td>
514 514 <a href="/coll/b/atom-log" title="subscribe to repository atom feed">
515 515 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
516 516 </a>
517 517 </td>
518 518 </tr>
519 519
520 520 <tr>
521 521 <td><a href="/coll/c/?style=paper">coll/c</a></td>
522 522 <td>unknown</td>
523 523 <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>
524 524 <td class="age">*</td> (glob)
525 525 <td class="indexlinks"></td>
526 526 <td>
527 527 <a href="/coll/c/atom-log" title="subscribe to repository atom feed">
528 528 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
529 529 </a>
530 530 </td>
531 531 </tr>
532 532
533 533 <tr>
534 534 <td><a href="/coll/notrepo/e/?style=paper">coll/notrepo/e</a></td>
535 535 <td>unknown</td>
536 536 <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>
537 537 <td class="age">*</td> (glob)
538 538 <td class="indexlinks"></td>
539 539 <td>
540 540 <a href="/coll/notrepo/e/atom-log" title="subscribe to repository atom feed">
541 541 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
542 542 </a>
543 543 </td>
544 544 </tr>
545 545
546 546 <tr>
547 547 <td><a href="/coll/notrepo/f/?style=paper">fancy name for repo f</a></td>
548 548 <td>unknown</td>
549 549 <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>
550 550 <td class="age">*</td> (glob)
551 551 <td class="indexlinks"></td>
552 552 <td>
553 553 <a href="/coll/notrepo/f/atom-log" title="subscribe to repository atom feed">
554 554 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
555 555 </a>
556 556 </td>
557 557 </tr>
558 558
559 559 <tr>
560 560 <td><a href="/rcoll/a/?style=paper">rcoll/a</a></td>
561 561 <td>unknown</td>
562 562 <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>
563 563 <td class="age">*</td> (glob)
564 564 <td class="indexlinks"></td>
565 565 <td>
566 566 <a href="/rcoll/a/atom-log" title="subscribe to repository atom feed">
567 567 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
568 568 </a>
569 569 </td>
570 570 </tr>
571 571
572 572 <tr>
573 573 <td><a href="/rcoll/a/.hg/patches/?style=paper">rcoll/a/.hg/patches</a></td>
574 574 <td>unknown</td>
575 575 <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>
576 576 <td class="age">*</td> (glob)
577 577 <td class="indexlinks"></td>
578 578 <td>
579 579 <a href="/rcoll/a/.hg/patches/atom-log" title="subscribe to repository atom feed">
580 580 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
581 581 </a>
582 582 </td>
583 583 </tr>
584 584
585 585 <tr>
586 586 <td><a href="/rcoll/b/?style=paper">rcoll/b</a></td>
587 587 <td>unknown</td>
588 588 <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>
589 589 <td class="age">*</td> (glob)
590 590 <td class="indexlinks"></td>
591 591 <td>
592 592 <a href="/rcoll/b/atom-log" title="subscribe to repository atom feed">
593 593 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
594 594 </a>
595 595 </td>
596 596 </tr>
597 597
598 598 <tr>
599 599 <td><a href="/rcoll/b/d/?style=paper">rcoll/b/d</a></td>
600 600 <td>unknown</td>
601 601 <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>
602 602 <td class="age">*</td> (glob)
603 603 <td class="indexlinks"></td>
604 604 <td>
605 605 <a href="/rcoll/b/d/atom-log" title="subscribe to repository atom feed">
606 606 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
607 607 </a>
608 608 </td>
609 609 </tr>
610 610
611 611 <tr>
612 612 <td><a href="/rcoll/c/?style=paper">rcoll/c</a></td>
613 613 <td>unknown</td>
614 614 <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>
615 615 <td class="age">*</td> (glob)
616 616 <td class="indexlinks"></td>
617 617 <td>
618 618 <a href="/rcoll/c/atom-log" title="subscribe to repository atom feed">
619 619 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
620 620 </a>
621 621 </td>
622 622 </tr>
623 623
624 624 <tr>
625 625 <td><a href="/rcoll/notrepo/e/?style=paper">rcoll/notrepo/e</a></td>
626 626 <td>unknown</td>
627 627 <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>
628 628 <td class="age">*</td> (glob)
629 629 <td class="indexlinks"></td>
630 630 <td>
631 631 <a href="/rcoll/notrepo/e/atom-log" title="subscribe to repository atom feed">
632 632 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
633 633 </a>
634 634 </td>
635 635 </tr>
636 636
637 637 <tr>
638 638 <td><a href="/rcoll/notrepo/e/e2/?style=paper">rcoll/notrepo/e/e2</a></td>
639 639 <td>unknown</td>
640 640 <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>
641 641 <td class="age">*</td> (glob)
642 642 <td class="indexlinks"></td>
643 643 <td>
644 644 <a href="/rcoll/notrepo/e/e2/atom-log" title="subscribe to repository atom feed">
645 645 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
646 646 </a>
647 647 </td>
648 648 </tr>
649 649
650 650 <tr>
651 651 <td><a href="/rcoll/notrepo/f/?style=paper">fancy name for repo f</a></td>
652 652 <td>unknown</td>
653 653 <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>
654 654 <td class="age">*</td> (glob)
655 655 <td class="indexlinks"></td>
656 656 <td>
657 657 <a href="/rcoll/notrepo/f/atom-log" title="subscribe to repository atom feed">
658 658 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
659 659 </a>
660 660 </td>
661 661 </tr>
662 662
663 663 <tr>
664 664 <td><a href="/rcoll/notrepo/f/f2/?style=paper">rcoll/notrepo/f/f2</a></td>
665 665 <td>unknown</td>
666 666 <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>
667 667 <td class="age">*</td> (glob)
668 668 <td class="indexlinks"></td>
669 669 <td>
670 670 <a href="/rcoll/notrepo/f/f2/atom-log" title="subscribe to repository atom feed">
671 671 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
672 672 </a>
673 673 </td>
674 674 </tr>
675 675
676 676 <tr>
677 677 <td><a href="/star/webdir/a/?style=paper">star/webdir/a</a></td>
678 678 <td>unknown</td>
679 679 <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>
680 680 <td class="age">*</td> (glob)
681 681 <td class="indexlinks"></td>
682 682 <td>
683 683 <a href="/star/webdir/a/atom-log" title="subscribe to repository atom feed">
684 684 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
685 685 </a>
686 686 </td>
687 687 </tr>
688 688
689 689 <tr>
690 690 <td><a href="/star/webdir/a/.hg/patches/?style=paper">star/webdir/a/.hg/patches</a></td>
691 691 <td>unknown</td>
692 692 <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>
693 693 <td class="age">*</td> (glob)
694 694 <td class="indexlinks"></td>
695 695 <td>
696 696 <a href="/star/webdir/a/.hg/patches/atom-log" title="subscribe to repository atom feed">
697 697 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
698 698 </a>
699 699 </td>
700 700 </tr>
701 701
702 702 <tr>
703 703 <td><a href="/star/webdir/b/?style=paper">star/webdir/b</a></td>
704 704 <td>unknown</td>
705 705 <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>
706 706 <td class="age">*</td> (glob)
707 707 <td class="indexlinks"></td>
708 708 <td>
709 709 <a href="/star/webdir/b/atom-log" title="subscribe to repository atom feed">
710 710 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
711 711 </a>
712 712 </td>
713 713 </tr>
714 714
715 715 <tr>
716 716 <td><a href="/star/webdir/c/?style=paper">star/webdir/c</a></td>
717 717 <td>unknown</td>
718 718 <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>
719 719 <td class="age">*</td> (glob)
720 720 <td class="indexlinks"></td>
721 721 <td>
722 722 <a href="/star/webdir/c/atom-log" title="subscribe to repository atom feed">
723 723 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
724 724 </a>
725 725 </td>
726 726 </tr>
727 727
728 728 <tr>
729 729 <td><a href="/star/webdir/notrepo/e/?style=paper">star/webdir/notrepo/e</a></td>
730 730 <td>unknown</td>
731 731 <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>
732 732 <td class="age">*</td> (glob)
733 733 <td class="indexlinks"></td>
734 734 <td>
735 735 <a href="/star/webdir/notrepo/e/atom-log" title="subscribe to repository atom feed">
736 736 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
737 737 </a>
738 738 </td>
739 739 </tr>
740 740
741 741 <tr>
742 742 <td><a href="/star/webdir/notrepo/f/?style=paper">fancy name for repo f</a></td>
743 743 <td>unknown</td>
744 744 <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>
745 745 <td class="age">*</td> (glob)
746 746 <td class="indexlinks"></td>
747 747 <td>
748 748 <a href="/star/webdir/notrepo/f/atom-log" title="subscribe to repository atom feed">
749 749 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
750 750 </a>
751 751 </td>
752 752 </tr>
753 753
754 754 <tr>
755 755 <td><a href="/starstar/webdir/a/?style=paper">starstar/webdir/a</a></td>
756 756 <td>unknown</td>
757 757 <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>
758 758 <td class="age">*</td> (glob)
759 759 <td class="indexlinks"></td>
760 760 <td>
761 761 <a href="/starstar/webdir/a/atom-log" title="subscribe to repository atom feed">
762 762 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
763 763 </a>
764 764 </td>
765 765 </tr>
766 766
767 767 <tr>
768 768 <td><a href="/starstar/webdir/a/.hg/patches/?style=paper">starstar/webdir/a/.hg/patches</a></td>
769 769 <td>unknown</td>
770 770 <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>
771 771 <td class="age">*</td> (glob)
772 772 <td class="indexlinks"></td>
773 773 <td>
774 774 <a href="/starstar/webdir/a/.hg/patches/atom-log" title="subscribe to repository atom feed">
775 775 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
776 776 </a>
777 777 </td>
778 778 </tr>
779 779
780 780 <tr>
781 781 <td><a href="/starstar/webdir/b/?style=paper">starstar/webdir/b</a></td>
782 782 <td>unknown</td>
783 783 <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>
784 784 <td class="age">*</td> (glob)
785 785 <td class="indexlinks"></td>
786 786 <td>
787 787 <a href="/starstar/webdir/b/atom-log" title="subscribe to repository atom feed">
788 788 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
789 789 </a>
790 790 </td>
791 791 </tr>
792 792
793 793 <tr>
794 794 <td><a href="/starstar/webdir/b/d/?style=paper">starstar/webdir/b/d</a></td>
795 795 <td>unknown</td>
796 796 <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>
797 797 <td class="age">*</td> (glob)
798 798 <td class="indexlinks"></td>
799 799 <td>
800 800 <a href="/starstar/webdir/b/d/atom-log" title="subscribe to repository atom feed">
801 801 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
802 802 </a>
803 803 </td>
804 804 </tr>
805 805
806 806 <tr>
807 807 <td><a href="/starstar/webdir/c/?style=paper">starstar/webdir/c</a></td>
808 808 <td>unknown</td>
809 809 <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>
810 810 <td class="age">*</td> (glob)
811 811 <td class="indexlinks"></td>
812 812 <td>
813 813 <a href="/starstar/webdir/c/atom-log" title="subscribe to repository atom feed">
814 814 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
815 815 </a>
816 816 </td>
817 817 </tr>
818 818
819 819 <tr>
820 820 <td><a href="/starstar/webdir/notrepo/e/?style=paper">starstar/webdir/notrepo/e</a></td>
821 821 <td>unknown</td>
822 822 <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>
823 823 <td class="age">*</td> (glob)
824 824 <td class="indexlinks"></td>
825 825 <td>
826 826 <a href="/starstar/webdir/notrepo/e/atom-log" title="subscribe to repository atom feed">
827 827 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
828 828 </a>
829 829 </td>
830 830 </tr>
831 831
832 832 <tr>
833 833 <td><a href="/starstar/webdir/notrepo/e/e2/?style=paper">starstar/webdir/notrepo/e/e2</a></td>
834 834 <td>unknown</td>
835 835 <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>
836 836 <td class="age">*</td> (glob)
837 837 <td class="indexlinks"></td>
838 838 <td>
839 839 <a href="/starstar/webdir/notrepo/e/e2/atom-log" title="subscribe to repository atom feed">
840 840 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
841 841 </a>
842 842 </td>
843 843 </tr>
844 844
845 845 <tr>
846 846 <td><a href="/starstar/webdir/notrepo/f/?style=paper">fancy name for repo f</a></td>
847 847 <td>unknown</td>
848 848 <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>
849 849 <td class="age">*</td> (glob)
850 850 <td class="indexlinks"></td>
851 851 <td>
852 852 <a href="/starstar/webdir/notrepo/f/atom-log" title="subscribe to repository atom feed">
853 853 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
854 854 </a>
855 855 </td>
856 856 </tr>
857 857
858 858 <tr>
859 859 <td><a href="/starstar/webdir/notrepo/f/f2/?style=paper">starstar/webdir/notrepo/f/f2</a></td>
860 860 <td>unknown</td>
861 861 <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>
862 862 <td class="age">*</td> (glob)
863 863 <td class="indexlinks"></td>
864 864 <td>
865 865 <a href="/starstar/webdir/notrepo/f/f2/atom-log" title="subscribe to repository atom feed">
866 866 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
867 867 </a>
868 868 </td>
869 869 </tr>
870 870
871 871 <tr>
872 872 <td><a href="/astar/?style=paper">astar</a></td>
873 873 <td>unknown</td>
874 874 <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>
875 875 <td class="age">*</td> (glob)
876 876 <td class="indexlinks"></td>
877 877 <td>
878 878 <a href="/astar/atom-log" title="subscribe to repository atom feed">
879 879 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
880 880 </a>
881 881 </td>
882 882 </tr>
883 883
884 884 <tr>
885 885 <td><a href="/astar/.hg/patches/?style=paper">astar/.hg/patches</a></td>
886 886 <td>unknown</td>
887 887 <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>
888 888 <td class="age">*</td> (glob)
889 889 <td class="indexlinks"></td>
890 890 <td>
891 891 <a href="/astar/.hg/patches/atom-log" title="subscribe to repository atom feed">
892 892 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
893 893 </a>
894 894 </td>
895 895 </tr>
896 896
897 897 </tbody>
898 898 </table>
899 899 </div>
900 900 </div>
901 901
902 902
903 903 </body>
904 904 </html>
905 905
906 906 $ get-with-headers.py localhost:$HGPORT1 't?style=raw'
907 907 200 Script output follows
908 908
909 909
910 910 /t/a/
911 911
912 912 $ get-with-headers.py localhost:$HGPORT1 't/?style=raw'
913 913 200 Script output follows
914 914
915 915
916 916 /t/a/
917 917
918 918 $ get-with-headers.py localhost:$HGPORT1 't/?style=paper'
919 919 200 Script output follows
920 920
921 921 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
922 922 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
923 923 <head>
924 924 <link rel="icon" href="/static/hgicon.png" type="image/png" />
925 925 <meta name="robots" content="index, nofollow" />
926 926 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
927 927 <script type="text/javascript" src="/static/mercurial.js"></script>
928 928
929 929 <title>Mercurial repositories index</title>
930 930 </head>
931 931 <body>
932 932
933 933 <div class="container">
934 934 <div class="menu">
935 935 <a href="https://mercurial-scm.org/">
936 936 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
937 937 </div>
938 938 <div class="main">
939 939 <h2 class="breadcrumb"><a href="/">Mercurial</a> &gt; <a href="/t">t</a> </h2>
940 940
941 941 <table class="bigtable">
942 942 <thead>
943 943 <tr>
944 944 <th><a href="?sort=name">Name</a></th>
945 945 <th><a href="?sort=description">Description</a></th>
946 946 <th><a href="?sort=contact">Contact</a></th>
947 947 <th><a href="?sort=lastchange">Last modified</a></th>
948 948 <th>&nbsp;</th>
949 949 <th>&nbsp;</th>
950 950 </tr>
951 951 </thead>
952 952 <tbody class="stripes2">
953 953
954 954 <tr>
955 955 <td><a href="/t/a/?style=paper">a</a></td>
956 956 <td>unknown</td>
957 957 <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>
958 958 <td class="age">*</td> (glob)
959 959 <td class="indexlinks"></td>
960 960 <td>
961 961 <a href="/t/a/atom-log" title="subscribe to repository atom feed">
962 962 <img class="atom-logo" src="/static/feed-icon-14x14.png" alt="subscribe to repository atom feed">
963 963 </a>
964 964 </td>
965 965 </tr>
966 966
967 967 </tbody>
968 968 </table>
969 969 </div>
970 970 </div>
971 971
972 972
973 973 </body>
974 974 </html>
975 975
976 976 $ get-with-headers.py localhost:$HGPORT1 't/a?style=atom'
977 977 200 Script output follows
978 978
979 979 <?xml version="1.0" encoding="ascii"?>
980 980 <feed xmlns="http://www.w3.org/2005/Atom">
981 981 <!-- Changelog -->
982 982 <id>http://*:$HGPORT1/t/a/</id> (glob)
983 983 <link rel="self" href="http://*:$HGPORT1/t/a/atom-log"/> (glob)
984 984 <link rel="alternate" href="http://*:$HGPORT1/t/a/"/> (glob)
985 985 <title>t/a Changelog</title>
986 986 <updated>1970-01-01T00:00:01+00:00</updated>
987 987
988 988 <entry>
989 989 <title>[default] a</title>
990 990 <id>http://*:$HGPORT1/t/a/#changeset-8580ff50825a50c8f716709acdf8de0deddcd6ab</id> (glob)
991 991 <link href="http://*:$HGPORT1/t/a/rev/8580ff50825a"/> (glob)
992 992 <author>
993 993 <name>test</name>
994 994 <email>&#116;&#101;&#115;&#116;</email>
995 995 </author>
996 996 <updated>1970-01-01T00:00:01+00:00</updated>
997 997 <published>1970-01-01T00:00:01+00:00</published>
998 998 <content type="xhtml">
999 999 <table xmlns="http://www.w3.org/1999/xhtml">
1000 1000 <tr>
1001 1001 <th style="text-align:left;">changeset</th>
1002 1002 <td>8580ff50825a</td>
1003 1003 </tr>
1004 1004 <tr>
1005 1005 <th style="text-align:left;">branch</th>
1006 1006 <td>default</td>
1007 1007 </tr>
1008 1008 <tr>
1009 1009 <th style="text-align:left;">bookmark</th>
1010 1010 <td></td>
1011 1011 </tr>
1012 1012 <tr>
1013 1013 <th style="text-align:left;">tag</th>
1014 1014 <td>tip</td>
1015 1015 </tr>
1016 1016 <tr>
1017 1017 <th style="text-align:left;">user</th>
1018 1018 <td>&#116;&#101;&#115;&#116;</td>
1019 1019 </tr>
1020 1020 <tr>
1021 1021 <th style="text-align:left;vertical-align:top;">description</th>
1022 1022 <td>a</td>
1023 1023 </tr>
1024 1024 <tr>
1025 1025 <th style="text-align:left;vertical-align:top;">files</th>
1026 1026 <td>a<br /></td>
1027 1027 </tr>
1028 1028 </table>
1029 1029 </content>
1030 1030 </entry>
1031 1031
1032 1032 </feed>
1033 1033 $ get-with-headers.py localhost:$HGPORT1 't/a/?style=atom'
1034 1034 200 Script output follows
1035 1035
1036 1036 <?xml version="1.0" encoding="ascii"?>
1037 1037 <feed xmlns="http://www.w3.org/2005/Atom">
1038 1038 <!-- Changelog -->
1039 1039 <id>http://*:$HGPORT1/t/a/</id> (glob)
1040 1040 <link rel="self" href="http://*:$HGPORT1/t/a/atom-log"/> (glob)
1041 1041 <link rel="alternate" href="http://*:$HGPORT1/t/a/"/> (glob)
1042 1042 <title>t/a Changelog</title>
1043 1043 <updated>1970-01-01T00:00:01+00:00</updated>
1044 1044
1045 1045 <entry>
1046 1046 <title>[default] a</title>
1047 1047 <id>http://*:$HGPORT1/t/a/#changeset-8580ff50825a50c8f716709acdf8de0deddcd6ab</id> (glob)
1048 1048 <link href="http://*:$HGPORT1/t/a/rev/8580ff50825a"/> (glob)
1049 1049 <author>
1050 1050 <name>test</name>
1051 1051 <email>&#116;&#101;&#115;&#116;</email>
1052 1052 </author>
1053 1053 <updated>1970-01-01T00:00:01+00:00</updated>
1054 1054 <published>1970-01-01T00:00:01+00:00</published>
1055 1055 <content type="xhtml">
1056 1056 <table xmlns="http://www.w3.org/1999/xhtml">
1057 1057 <tr>
1058 1058 <th style="text-align:left;">changeset</th>
1059 1059 <td>8580ff50825a</td>
1060 1060 </tr>
1061 1061 <tr>
1062 1062 <th style="text-align:left;">branch</th>
1063 1063 <td>default</td>
1064 1064 </tr>
1065 1065 <tr>
1066 1066 <th style="text-align:left;">bookmark</th>
1067 1067 <td></td>
1068 1068 </tr>
1069 1069 <tr>
1070 1070 <th style="text-align:left;">tag</th>
1071 1071 <td>tip</td>
1072 1072 </tr>
1073 1073 <tr>
1074 1074 <th style="text-align:left;">user</th>
1075 1075 <td>&#116;&#101;&#115;&#116;</td>
1076 1076 </tr>
1077 1077 <tr>
1078 1078 <th style="text-align:left;vertical-align:top;">description</th>
1079 1079 <td>a</td>
1080 1080 </tr>
1081 1081 <tr>
1082 1082 <th style="text-align:left;vertical-align:top;">files</th>
1083 1083 <td>a<br /></td>
1084 1084 </tr>
1085 1085 </table>
1086 1086 </content>
1087 1087 </entry>
1088 1088
1089 1089 </feed>
1090 1090 $ get-with-headers.py localhost:$HGPORT1 't/a/file/tip/a?style=raw'
1091 1091 200 Script output follows
1092 1092
1093 1093 a
1094 1094
1095 1095 Test [paths] '*' extension
1096 1096
1097 1097 $ get-with-headers.py localhost:$HGPORT1 'coll/?style=raw'
1098 1098 200 Script output follows
1099 1099
1100 1100
1101 1101 /coll/a/
1102 1102 /coll/a/.hg/patches/
1103 1103 /coll/b/
1104 1104 /coll/c/
1105 1105 /coll/notrepo/e/
1106 1106 /coll/notrepo/f/
1107 1107
1108 1108 $ get-with-headers.py localhost:$HGPORT1 'coll/a/file/tip/a?style=raw'
1109 1109 200 Script output follows
1110 1110
1111 1111 a
1112 1112
1113 1113 Test [paths] '**' extension
1114 1114
1115 1115 $ get-with-headers.py localhost:$HGPORT1 'rcoll/?style=raw'
1116 1116 200 Script output follows
1117 1117
1118 1118
1119 1119 /rcoll/a/
1120 1120 /rcoll/a/.hg/patches/
1121 1121 /rcoll/b/
1122 1122 /rcoll/b/d/
1123 1123 /rcoll/c/
1124 1124 /rcoll/notrepo/e/
1125 1125 /rcoll/notrepo/e/e2/
1126 1126 /rcoll/notrepo/f/
1127 1127 /rcoll/notrepo/f/f2/
1128 1128
1129 1129 $ get-with-headers.py localhost:$HGPORT1 'rcoll/b/d/file/tip/d?style=raw'
1130 1130 200 Script output follows
1131 1131
1132 1132 d
1133 1133
1134 1134 Test collapse = True
1135 1135
1136 1136 $ killdaemons.py
1137 1137 $ cat >> paths.conf <<EOF
1138 1138 > [web]
1139 1139 > collapse=true
1140 1140 > descend = true
1141 1141 > EOF
1142 1142 $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
1143 1143 > -A access-paths.log -E error-paths-3.log
1144 1144 $ cat hg.pid >> $DAEMON_PIDS
1145 1145 $ get-with-headers.py localhost:$HGPORT1 'coll/?style=raw'
1146 1146 200 Script output follows
1147 1147
1148 1148
1149 1149 /coll/a/
1150 1150 /coll/a/.hg/patches/
1151 1151 /coll/b/
1152 1152 /coll/c/
1153 1153 /coll/notrepo/
1154 1154
1155 1155 $ get-with-headers.py localhost:$HGPORT1 'coll/a/file/tip/a?style=raw'
1156 1156 200 Script output follows
1157 1157
1158 1158 a
1159 1159 $ get-with-headers.py localhost:$HGPORT1 'rcoll/?style=raw'
1160 1160 200 Script output follows
1161 1161
1162 1162
1163 1163 /rcoll/a/
1164 1164 /rcoll/a/.hg/patches/
1165 1165 /rcoll/b/
1166 1166 /rcoll/b/d/
1167 1167 /rcoll/c/
1168 1168 /rcoll/notrepo/
1169 1169
1170 1170 $ get-with-headers.py localhost:$HGPORT1 'rcoll/b/d/file/tip/d?style=raw'
1171 1171 200 Script output follows
1172 1172
1173 1173 d
1174 1174
1175 1175 Test intermediate directories
1176 1176
1177 1177 Hide the subrepo parent
1178 1178
1179 1179 $ cp $root/notrepo/f/.hg/hgrc $root/notrepo/f/.hg/hgrc.bak
1180 1180 $ cat >> $root/notrepo/f/.hg/hgrc << EOF
1181 1181 > [web]
1182 1182 > hidden = True
1183 1183 > EOF
1184 1184
1185 1185 $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/?style=raw'
1186 1186 200 Script output follows
1187 1187
1188 1188
1189 1189 /rcoll/notrepo/e/
1190 1190 /rcoll/notrepo/e/e2/
1191 1191
1192 1192
1193 1193 Subrepo parent not hidden
1194 1194 $ mv $root/notrepo/f/.hg/hgrc.bak $root/notrepo/f/.hg/hgrc
1195 1195
1196 1196 $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/?style=raw'
1197 1197 200 Script output follows
1198 1198
1199 1199
1200 1200 /rcoll/notrepo/e/
1201 1201 /rcoll/notrepo/e/e2/
1202 1202 /rcoll/notrepo/f/
1203 1203 /rcoll/notrepo/f/f2/
1204 1204
1205 1205
1206 1206 Test repositories inside intermediate directories
1207 1207
1208 1208 $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/e/file/tip/e?style=raw'
1209 1209 200 Script output follows
1210 1210
1211 1211 e
1212 1212
1213 1213 Test subrepositories inside intermediate directories
1214 1214
1215 1215 $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/f/f2/file/tip/f2?style=raw'
1216 1216 200 Script output follows
1217 1217
1218 1218 f2
1219 1219
1220 1220 Test descend = False
1221 1221
1222 1222 $ killdaemons.py
1223 1223 $ cat >> paths.conf <<EOF
1224 1224 > descend=false
1225 1225 > EOF
1226 1226 $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
1227 1227 > -A access-paths.log -E error-paths-4.log
1228 1228 $ cat hg.pid >> $DAEMON_PIDS
1229 1229 $ get-with-headers.py localhost:$HGPORT1 'coll/?style=raw'
1230 1230 200 Script output follows
1231 1231
1232 1232
1233 1233 /coll/a/
1234 1234 /coll/b/
1235 1235 /coll/c/
1236 1236
1237 1237 $ get-with-headers.py localhost:$HGPORT1 'coll/a/file/tip/a?style=raw'
1238 1238 200 Script output follows
1239 1239
1240 1240 a
1241 1241 $ get-with-headers.py localhost:$HGPORT1 'rcoll/?style=raw'
1242 1242 200 Script output follows
1243 1243
1244 1244
1245 1245 /rcoll/a/
1246 1246 /rcoll/b/
1247 1247 /rcoll/c/
1248 1248
1249 1249 $ get-with-headers.py localhost:$HGPORT1 'rcoll/b/d/file/tip/d?style=raw'
1250 1250 200 Script output follows
1251 1251
1252 1252 d
1253 1253
1254 1254 Test intermediate directories
1255 1255
1256 1256 $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/?style=raw'
1257 1257 200 Script output follows
1258 1258
1259 1259
1260 1260 /rcoll/notrepo/e/
1261 1261 /rcoll/notrepo/f/
1262 1262
1263 1263
1264 1264 Test repositories inside intermediate directories
1265 1265
1266 1266 $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/e/file/tip/e?style=raw'
1267 1267 200 Script output follows
1268 1268
1269 1269 e
1270 1270
1271 1271 Test subrepositories inside intermediate directories
1272 1272
1273 1273 $ get-with-headers.py localhost:$HGPORT1 'rcoll/notrepo/f/f2/file/tip/f2?style=raw'
1274 1274 200 Script output follows
1275 1275
1276 1276 f2
1277 1277
1278 1278 Test [paths] '*' in a repo root
1279 1279
1280 1280 $ hg id http://localhost:$HGPORT1/astar
1281 1281 8580ff50825a
1282 1282
1283 1283 $ killdaemons.py
1284 1284 $ cat > paths.conf <<EOF
1285 1285 > [paths]
1286 1286 > t/a = $root/a
1287 1287 > t/b = $root/b
1288 1288 > c = $root/c
1289 1289 > EOF
1290 1290 $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
1291 1291 > -A access-paths.log -E error-paths-5.log
1292 1292 $ cat hg.pid >> $DAEMON_PIDS
1293 1293 $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
1294 1294 200 Script output follows
1295 1295
1296 1296
1297 1297 /t/a/
1298 1298 /t/b/
1299 1299 /c/
1300 1300
1301 1301 $ get-with-headers.py localhost:$HGPORT1 't/?style=raw'
1302 1302 200 Script output follows
1303 1303
1304 1304
1305 1305 /t/a/
1306 1306 /t/b/
1307 1307
1308 1308
1309 1309 Test collapse = True
1310 1310
1311 1311 $ killdaemons.py
1312 1312 $ cat >> paths.conf <<EOF
1313 1313 > [web]
1314 1314 > collapse=true
1315 1315 > EOF
1316 1316 $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
1317 1317 > -A access-paths.log -E error-paths-6.log
1318 1318 $ cat hg.pid >> $DAEMON_PIDS
1319 1319 $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
1320 1320 200 Script output follows
1321 1321
1322 1322
1323 1323 /t/
1324 1324 /c/
1325 1325
1326 1326 $ get-with-headers.py localhost:$HGPORT1 't/?style=raw'
1327 1327 200 Script output follows
1328 1328
1329 1329
1330 1330 /t/a/
1331 1331 /t/b/
1332 1332
1333 1333
1334 1334 test descend = False
1335 1335
1336 1336 $ killdaemons.py
1337 1337 $ cat >> paths.conf <<EOF
1338 1338 > descend=false
1339 1339 > EOF
1340 1340 $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
1341 1341 > -A access-paths.log -E error-paths-7.log
1342 1342 $ cat hg.pid >> $DAEMON_PIDS
1343 1343 $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
1344 1344 200 Script output follows
1345 1345
1346 1346
1347 1347 /c/
1348 1348
1349 1349 $ get-with-headers.py localhost:$HGPORT1 't/?style=raw'
1350 1350 200 Script output follows
1351 1351
1352 1352
1353 1353 /t/a/
1354 1354 /t/b/
1355 1355
1356 1356 $ killdaemons.py
1357 1357 $ cat > paths.conf <<EOF
1358 1358 > [paths]
1359 1359 > nostore = $root/nostore
1360 1360 > inexistent = $root/inexistent
1361 1361 > EOF
1362 1362 $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
1363 1363 > -A access-paths.log -E error-paths-8.log
1364 1364 $ cat hg.pid >> $DAEMON_PIDS
1365 1365
1366 1366 test inexistent and inaccessible repo should be ignored silently
1367 1367
1368 1368 $ get-with-headers.py localhost:$HGPORT1 ''
1369 1369 200 Script output follows
1370 1370
1371 1371 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1372 1372 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
1373 1373 <head>
1374 1374 <link rel="icon" href="/static/hgicon.png" type="image/png" />
1375 1375 <meta name="robots" content="index, nofollow" />
1376 1376 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
1377 1377 <script type="text/javascript" src="/static/mercurial.js"></script>
1378 1378
1379 1379 <title>Mercurial repositories index</title>
1380 1380 </head>
1381 1381 <body>
1382 1382
1383 1383 <div class="container">
1384 1384 <div class="menu">
1385 1385 <a href="https://mercurial-scm.org/">
1386 1386 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
1387 1387 </div>
1388 1388 <div class="main">
1389 1389 <h2 class="breadcrumb"><a href="/">Mercurial</a> </h2>
1390 1390
1391 1391 <table class="bigtable">
1392 1392 <thead>
1393 1393 <tr>
1394 1394 <th><a href="?sort=name">Name</a></th>
1395 1395 <th><a href="?sort=description">Description</a></th>
1396 1396 <th><a href="?sort=contact">Contact</a></th>
1397 1397 <th><a href="?sort=lastchange">Last modified</a></th>
1398 1398 <th>&nbsp;</th>
1399 1399 <th>&nbsp;</th>
1400 1400 </tr>
1401 1401 </thead>
1402 1402 <tbody class="stripes2">
1403 1403
1404 1404 </tbody>
1405 1405 </table>
1406 1406 </div>
1407 1407 </div>
1408 1408
1409 1409
1410 1410 </body>
1411 1411 </html>
1412 1412
1413 1413
1414 1414 test listening address/port specified by web-conf (issue4699):
1415 1415
1416 1416 $ killdaemons.py
1417 1417 $ cat >> paths.conf <<EOF
1418 1418 > [web]
1419 1419 > address = localhost
1420 1420 > port = $HGPORT1
1421 1421 > EOF
1422 1422 $ hg serve -d --pid-file=hg.pid --web-conf paths.conf \
1423 1423 > -A access-paths.log -E error-paths-9.log
1424 1424 listening at http://*:$HGPORT1/ (bound to *$LOCALIP*:$HGPORT1) (glob) (?)
1425 1425 $ cat hg.pid >> $DAEMON_PIDS
1426 1426 $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
1427 1427 200 Script output follows
1428 1428
1429 1429
1430 1430
1431 1431 test --port option overrides web.port:
1432 1432
1433 1433 $ killdaemons.py
1434 1434 $ hg serve -p $HGPORT2 -d -v --pid-file=hg.pid --web-conf paths.conf \
1435 1435 > -A access-paths.log -E error-paths-10.log
1436 1436 listening at http://*:$HGPORT2/ (bound to *$LOCALIP*:$HGPORT2) (glob) (?)
1437 1437 $ cat hg.pid >> $DAEMON_PIDS
1438 1438 $ get-with-headers.py localhost:$HGPORT2 '?style=raw'
1439 1439 200 Script output follows
1440 1440
1441 1441
1442 1442
1443 1443
1444 1444 $ killdaemons.py
1445 1445 $ cat > collections.conf <<EOF
1446 1446 > [collections]
1447 1447 > $root=$root
1448 1448 > EOF
1449 1449 $ hg serve --config web.baseurl=http://hg.example.com:8080/ -p $HGPORT2 -d \
1450 1450 > --pid-file=hg.pid --webdir-conf collections.conf \
1451 1451 > -A access-collections.log -E error-collections.log
1452 1452 $ cat hg.pid >> $DAEMON_PIDS
1453 1453
1454 1454 collections: should succeed
1455 1455
1456 1456 $ get-with-headers.py localhost:$HGPORT2 '?style=raw'
1457 1457 200 Script output follows
1458 1458
1459 1459
1460 1460 /a/
1461 1461 /a/.hg/patches/
1462 1462 /b/
1463 1463 /c/
1464 1464 /notrepo/e/
1465 1465 /notrepo/f/
1466 1466
1467 1467 $ get-with-headers.py localhost:$HGPORT2 'a/file/tip/a?style=raw'
1468 1468 200 Script output follows
1469 1469
1470 1470 a
1471 1471 $ get-with-headers.py localhost:$HGPORT2 'b/file/tip/b?style=raw'
1472 1472 200 Script output follows
1473 1473
1474 1474 b
1475 1475 $ get-with-headers.py localhost:$HGPORT2 'c/file/tip/c?style=raw'
1476 1476 200 Script output follows
1477 1477
1478 1478 c
1479 1479
1480 1480 atom-log with basedir /
1481 1481
1482 1482 $ get-with-headers.py localhost:$HGPORT2 'a/atom-log' | grep '<link'
1483 1483 <link rel="self" href="http://hg.example.com:8080/a/atom-log"/>
1484 1484 <link rel="alternate" href="http://hg.example.com:8080/a/"/>
1485 1485 <link href="http://hg.example.com:8080/a/rev/8580ff50825a"/>
1486 1486
1487 1487 rss-log with basedir /
1488 1488
1489 1489 $ get-with-headers.py localhost:$HGPORT2 'a/rss-log' | grep '<guid'
1490 1490 <guid isPermaLink="true">http://hg.example.com:8080/a/rev/8580ff50825a</guid>
1491 1491 $ killdaemons.py
1492 1492 $ hg serve --config web.baseurl=http://hg.example.com:8080/foo/ -p $HGPORT2 -d \
1493 1493 > --pid-file=hg.pid --webdir-conf collections.conf \
1494 1494 > -A access-collections-2.log -E error-collections-2.log
1495 1495 $ cat hg.pid >> $DAEMON_PIDS
1496 1496
1497 1497 atom-log with basedir /foo/
1498 1498
1499 1499 $ get-with-headers.py localhost:$HGPORT2 'a/atom-log' | grep '<link'
1500 1500 <link rel="self" href="http://hg.example.com:8080/foo/a/atom-log"/>
1501 1501 <link rel="alternate" href="http://hg.example.com:8080/foo/a/"/>
1502 1502 <link href="http://hg.example.com:8080/foo/a/rev/8580ff50825a"/>
1503 1503
1504 1504 rss-log with basedir /foo/
1505 1505
1506 1506 $ get-with-headers.py localhost:$HGPORT2 'a/rss-log' | grep '<guid'
1507 1507 <guid isPermaLink="true">http://hg.example.com:8080/foo/a/rev/8580ff50825a</guid>
1508 1508
1509 1509 Path refreshing works as expected
1510 1510
1511 1511 $ killdaemons.py
1512 1512 $ mkdir $root/refreshtest
1513 1513 $ hg init $root/refreshtest/a
1514 1514 $ cat > paths.conf << EOF
1515 1515 > [paths]
1516 1516 > / = $root/refreshtest/*
1517 1517 > EOF
1518 1518 $ hg serve -p $HGPORT1 -d --pid-file hg.pid --webdir-conf paths.conf
1519 1519 $ cat hg.pid >> $DAEMON_PIDS
1520 1520
1521 1521 $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
1522 1522 200 Script output follows
1523 1523
1524 1524
1525 1525 /a/
1526 1526
1527 1527
1528 1528 By default refreshing occurs every 20s and a new repo won't be listed
1529 1529 immediately.
1530 1530
1531 1531 $ hg init $root/refreshtest/b
1532 1532 $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
1533 1533 200 Script output follows
1534 1534
1535 1535
1536 1536 /a/
1537 1537
1538 1538
1539 1539 Restart the server with no refresh interval. New repo should appear
1540 1540 immediately.
1541 1541
1542 1542 $ killdaemons.py
1543 1543 $ cat > paths.conf << EOF
1544 1544 > [web]
1545 1545 > refreshinterval = -1
1546 1546 > [paths]
1547 1547 > / = $root/refreshtest/*
1548 1548 > EOF
1549 1549 $ hg serve -p $HGPORT1 -d --pid-file hg.pid --webdir-conf paths.conf
1550 1550 $ cat hg.pid >> $DAEMON_PIDS
1551 1551
1552 1552 $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
1553 1553 200 Script output follows
1554 1554
1555 1555
1556 1556 /a/
1557 1557 /b/
1558 1558
1559 1559
1560 1560 $ hg init $root/refreshtest/c
1561 1561 $ get-with-headers.py localhost:$HGPORT1 '?style=raw'
1562 1562 200 Script output follows
1563 1563
1564 1564
1565 1565 /a/
1566 1566 /b/
1567 1567 /c/
1568 1568
1569 1569 $ killdaemons.py
1570 1570 $ cat > paths.conf << EOF
1571 1571 > [paths]
1572 1572 > /dir1/a_repo = $root/a
1573 1573 > /dir1/a_repo/b_repo = $root/b
1574 1574 > /dir1/dir2/index = $root/b
1575 1575 > EOF
1576 1576 $ hg serve -p $HGPORT1 -d --pid-file hg.pid --webdir-conf paths.conf
1577 1577 $ cat hg.pid >> $DAEMON_PIDS
1578 1578
1579 1579 $ echo 'index file' > $root/a/index
1580 1580 $ hg --cwd $root/a ci -Am 'add index file'
1581 1581 adding index
1582 1582
1583 1583 $ get-with-headers.py localhost:$HGPORT1 '' | grep 'a_repo'
1584 1584 <td><a href="/dir1/a_repo/">dir1/a_repo</a></td>
1585 1585 <a href="/dir1/a_repo/atom-log" title="subscribe to repository atom feed">
1586 1586 <td><a href="/dir1/a_repo/b_repo/">dir1/a_repo/b_repo</a></td>
1587 1587 <a href="/dir1/a_repo/b_repo/atom-log" title="subscribe to repository atom feed">
1588 1588
1589 1589 $ get-with-headers.py localhost:$HGPORT1 'index' | grep 'a_repo'
1590 1590 <td><a href="/dir1/a_repo/">dir1/a_repo</a></td>
1591 1591 <a href="/dir1/a_repo/atom-log" title="subscribe to repository atom feed">
1592 1592 <td><a href="/dir1/a_repo/b_repo/">dir1/a_repo/b_repo</a></td>
1593 1593 <a href="/dir1/a_repo/b_repo/atom-log" title="subscribe to repository atom feed">
1594 1594
1595 1595 $ get-with-headers.py localhost:$HGPORT1 'dir1' | grep 'a_repo'
1596 1596 <td><a href="/dir1/a_repo/">a_repo</a></td>
1597 1597 <a href="/dir1/a_repo/atom-log" title="subscribe to repository atom feed">
1598 1598 <td><a href="/dir1/a_repo/b_repo/">a_repo/b_repo</a></td>
1599 1599 <a href="/dir1/a_repo/b_repo/atom-log" title="subscribe to repository atom feed">
1600 1600
1601 1601 $ get-with-headers.py localhost:$HGPORT1 'dir1/index' | grep 'a_repo'
1602 1602 <td><a href="/dir1/a_repo/">a_repo</a></td>
1603 1603 <a href="/dir1/a_repo/atom-log" title="subscribe to repository atom feed">
1604 1604 <td><a href="/dir1/a_repo/b_repo/">a_repo/b_repo</a></td>
1605 1605 <a href="/dir1/a_repo/b_repo/atom-log" title="subscribe to repository atom feed">
1606 1606
1607 1607 $ get-with-headers.py localhost:$HGPORT1 'dir1/a_repo' | grep 'a_repo'
1608 1608 <link rel="icon" href="/dir1/a_repo/static/hgicon.png" type="image/png" />
1609 1609 <link rel="stylesheet" href="/dir1/a_repo/static/style-paper.css" type="text/css" />
1610 1610 <script type="text/javascript" src="/dir1/a_repo/static/mercurial.js"></script>
1611 1611 <title>dir1/a_repo: log</title>
1612 1612 href="/dir1/a_repo/atom-log" title="Atom feed for dir1/a_repo" />
1613 1613 href="/dir1/a_repo/rss-log" title="RSS feed for dir1/a_repo" />
1614 1614 <img src="/dir1/a_repo/static/hglogo.png" alt="mercurial" /></a>
1615 1615 <li><a href="/dir1/a_repo/graph/tip">graph</a></li>
1616 1616 <li><a href="/dir1/a_repo/tags">tags</a></li>
1617 1617 <li><a href="/dir1/a_repo/bookmarks">bookmarks</a></li>
1618 1618 <li><a href="/dir1/a_repo/branches">branches</a></li>
1619 1619 <li><a href="/dir1/a_repo/rev/tip">changeset</a></li>
1620 1620 <li><a href="/dir1/a_repo/file/tip">browse</a></li>
1621 1621 <li><a href="/dir1/a_repo/help">help</a></li>
1622 1622 <a href="/dir1/a_repo/atom-log" title="subscribe to atom feed">
1623 1623 <img class="atom-logo" src="/dir1/a_repo/static/feed-icon-14x14.png" alt="atom feed" />
1624 1624 <h2 class="breadcrumb"><a href="/">Mercurial</a> &gt; <a href="/dir1">dir1</a> &gt; <a href="/dir1/a_repo">a_repo</a> </h2>
1625 1625 <form class="search" action="/dir1/a_repo/log">
1626 1626 number or hash, or <a href="/dir1/a_repo/help/revsets">revset expression</a>.</div>
1627 1627 <a href="/dir1/a_repo/shortlog/tip?revcount=30">less</a>
1628 1628 <a href="/dir1/a_repo/shortlog/tip?revcount=120">more</a>
1629 1629 | rev 1: <a href="/dir1/a_repo/shortlog/8580ff50825a">(0)</a> <a href="/dir1/a_repo/shortlog/tip">tip</a>
1630 1630 <a href="/dir1/a_repo/rev/71a89161f014">add index file</a>
1631 1631 <a href="/dir1/a_repo/rev/8580ff50825a">a</a>
1632 1632 <a href="/dir1/a_repo/shortlog/tip?revcount=30">less</a>
1633 1633 <a href="/dir1/a_repo/shortlog/tip?revcount=120">more</a>
1634 1634 | rev 1: <a href="/dir1/a_repo/shortlog/8580ff50825a">(0)</a> <a href="/dir1/a_repo/shortlog/tip">tip</a>
1635 1635 '/dir1/a_repo/shortlog/%next%',
1636 1636
1637 1637 $ get-with-headers.py localhost:$HGPORT1 'dir1/a_repo/index' | grep 'a_repo'
1638 1638 <h2 class="breadcrumb"><a href="/">Mercurial</a> &gt; <a href="/dir1">dir1</a> &gt; <a href="/dir1/a_repo">a_repo</a> </h2>
1639 1639 <td><a href="/dir1/a_repo/b_repo/">b_repo</a></td>
1640 1640 <a href="/dir1/a_repo/b_repo/atom-log" title="subscribe to repository atom feed">
1641 1641
1642 1642 Files named 'index' are not blocked
1643 1643
1644 1644 $ get-with-headers.py localhost:$HGPORT1 'dir1/a_repo/raw-file/tip/index'
1645 1645 200 Script output follows
1646 1646
1647 1647 index file
1648 1648
1649 1649 Repos named 'index' take precedence over the index file
1650 1650
1651 1651 $ get-with-headers.py localhost:$HGPORT1 'dir1/dir2/index' | grep 'index'
1652 1652 <link rel="icon" href="/dir1/dir2/index/static/hgicon.png" type="image/png" />
1653 1653 <meta name="robots" content="index, nofollow" />
1654 1654 <link rel="stylesheet" href="/dir1/dir2/index/static/style-paper.css" type="text/css" />
1655 1655 <script type="text/javascript" src="/dir1/dir2/index/static/mercurial.js"></script>
1656 1656 <title>dir1/dir2/index: log</title>
1657 1657 href="/dir1/dir2/index/atom-log" title="Atom feed for dir1/dir2/index" />
1658 1658 href="/dir1/dir2/index/rss-log" title="RSS feed for dir1/dir2/index" />
1659 1659 <img src="/dir1/dir2/index/static/hglogo.png" alt="mercurial" /></a>
1660 1660 <li><a href="/dir1/dir2/index/graph/tip">graph</a></li>
1661 1661 <li><a href="/dir1/dir2/index/tags">tags</a></li>
1662 1662 <li><a href="/dir1/dir2/index/bookmarks">bookmarks</a></li>
1663 1663 <li><a href="/dir1/dir2/index/branches">branches</a></li>
1664 1664 <li><a href="/dir1/dir2/index/rev/tip">changeset</a></li>
1665 1665 <li><a href="/dir1/dir2/index/file/tip">browse</a></li>
1666 1666 <li><a href="/dir1/dir2/index/help">help</a></li>
1667 1667 <a href="/dir1/dir2/index/atom-log" title="subscribe to atom feed">
1668 1668 <img class="atom-logo" src="/dir1/dir2/index/static/feed-icon-14x14.png" alt="atom feed" />
1669 1669 <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>
1670 1670 <form class="search" action="/dir1/dir2/index/log">
1671 1671 number or hash, or <a href="/dir1/dir2/index/help/revsets">revset expression</a>.</div>
1672 1672 <a href="/dir1/dir2/index/shortlog/tip?revcount=30">less</a>
1673 1673 <a href="/dir1/dir2/index/shortlog/tip?revcount=120">more</a>
1674 1674 | rev 0: <a href="/dir1/dir2/index/shortlog/39505516671b">(0)</a> <a href="/dir1/dir2/index/shortlog/tip">tip</a>
1675 1675 <a href="/dir1/dir2/index/rev/39505516671b">b</a>
1676 1676 <a href="/dir1/dir2/index/shortlog/tip?revcount=30">less</a>
1677 1677 <a href="/dir1/dir2/index/shortlog/tip?revcount=120">more</a>
1678 1678 | rev 0: <a href="/dir1/dir2/index/shortlog/39505516671b">(0)</a> <a href="/dir1/dir2/index/shortlog/tip">tip</a>
1679 1679 '/dir1/dir2/index/shortlog/%next%',
1680 1680
1681 1681 $ killdaemons.py
1682 1682
1683 $ cat > paths.conf << EOF
1684 > [paths]
1685 > / = $root/a
1686 > EOF
1687 $ hg serve -p $HGPORT1 -d --pid-file hg.pid --webdir-conf paths.conf
1688 $ cat hg.pid >> $DAEMON_PIDS
1689
1690 $ hg id http://localhost:$HGPORT1
1691 71a89161f014
1692
1693 $ get-with-headers.py localhost:$HGPORT1 '' | grep 'index'
1694 <meta name="robots" content="index, nofollow" />
1695 <a href="/rev/71a89161f014">add index file</a>
1696
1697 $ killdaemons.py
1698
1683 1699 paths errors 1
1684 1700
1685 1701 $ cat error-paths-1.log
1686 1702
1687 1703 paths errors 2
1688 1704
1689 1705 $ cat error-paths-2.log
1690 1706
1691 1707 paths errors 3
1692 1708
1693 1709 $ cat error-paths-3.log
1694 1710
1695 1711 paths errors 4
1696 1712
1697 1713 $ cat error-paths-4.log
1698 1714
1699 1715 paths errors 5
1700 1716
1701 1717 $ cat error-paths-5.log
1702 1718
1703 1719 paths errors 6
1704 1720
1705 1721 $ cat error-paths-6.log
1706 1722
1707 1723 paths errors 7
1708 1724
1709 1725 $ cat error-paths-7.log
1710 1726
1711 1727 paths errors 8
1712 1728
1713 1729 $ cat error-paths-8.log
1714 1730
1715 1731 paths errors 9
1716 1732
1717 1733 $ cat error-paths-9.log
1718 1734
1719 1735 paths errors 10
1720 1736
1721 1737 $ cat error-paths-10.log
1722 1738
1723 1739 collections errors
1724 1740
1725 1741 $ cat error-collections.log
1726 1742
1727 1743 collections errors 2
1728 1744
1729 1745 $ cat error-collections-2.log
General Comments 0
You need to be logged in to leave comments. Login now