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