##// END OF EJS Templates
Merge with crew-stable
Bryan O'Sullivan -
r9199:fb5562af merge default
parent child Browse files
Show More
@@ -1,331 +1,333 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, incorporated herein by reference.
7 # GNU General Public License version 2, incorporated herein by reference.
8
8
9 import os, time
9 import os, re, time
10 from mercurial.i18n import _
10 from mercurial.i18n import _
11 from mercurial import ui, hg, util, templater
11 from mercurial import ui, hg, util, templater
12 from mercurial import error, encoding
12 from mercurial import error, encoding
13 from common import ErrorResponse, get_mtime, staticfile, paritygen,\
13 from common import ErrorResponse, get_mtime, staticfile, paritygen,\
14 get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
14 get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
15 from hgweb_mod import hgweb
15 from hgweb_mod import hgweb
16 from request import wsgirequest
16 from request import wsgirequest
17 import webutil
17 import webutil
18
18
19 def cleannames(items):
19 def cleannames(items):
20 return [(util.pconvert(name).strip('/'), path) for name, path in items]
20 return [(util.pconvert(name).strip('/'), path) for name, path in items]
21
21
22 def findrepos(paths):
22 def findrepos(paths):
23 repos = {}
23 repos = {}
24 for prefix, root in cleannames(paths):
24 for prefix, root in cleannames(paths):
25 roothead, roottail = os.path.split(root)
25 roothead, roottail = os.path.split(root)
26 # "foo = /bar/*" makes every subrepo of /bar/ to be
26 # "foo = /bar/*" makes every subrepo of /bar/ to be
27 # mounted as foo/subrepo
27 # mounted as foo/subrepo
28 # and "foo = /bar/**" also recurses into the subdirectories,
28 # and "foo = /bar/**" also recurses into the subdirectories,
29 # remember to use it without working dir.
29 # remember to use it without working dir.
30 try:
30 try:
31 recurse = {'*': False, '**': True}[roottail]
31 recurse = {'*': False, '**': True}[roottail]
32 except KeyError:
32 except KeyError:
33 repos[prefix] = root
33 repos[prefix] = root
34 continue
34 continue
35 roothead = os.path.normpath(roothead)
35 roothead = os.path.normpath(roothead)
36 for path in util.walkrepos(roothead, followsym=True, recurse=recurse):
36 for path in util.walkrepos(roothead, followsym=True, recurse=recurse):
37 path = os.path.normpath(path)
37 path = os.path.normpath(path)
38 name = util.pconvert(path[len(roothead):]).strip('/')
38 name = util.pconvert(path[len(roothead):]).strip('/')
39 if prefix:
39 if prefix:
40 name = prefix + '/' + name
40 name = prefix + '/' + name
41 repos[name] = path
41 repos[name] = path
42 return repos.items()
42 return repos.items()
43
43
44 class hgwebdir(object):
44 class hgwebdir(object):
45 refreshinterval = 20
45 refreshinterval = 20
46
46
47 def __init__(self, conf, baseui=None):
47 def __init__(self, conf, baseui=None):
48 self.conf = conf
48 self.conf = conf
49 self.baseui = baseui
49 self.baseui = baseui
50 self.lastrefresh = 0
50 self.lastrefresh = 0
51 self.refresh()
51 self.refresh()
52
52
53 def refresh(self):
53 def refresh(self):
54 if self.lastrefresh + self.refreshinterval > time.time():
54 if self.lastrefresh + self.refreshinterval > time.time():
55 return
55 return
56
56
57 if self.baseui:
57 if self.baseui:
58 self.ui = self.baseui.copy()
58 self.ui = self.baseui.copy()
59 else:
59 else:
60 self.ui = ui.ui()
60 self.ui = ui.ui()
61 self.ui.setconfig('ui', 'report_untrusted', 'off')
61 self.ui.setconfig('ui', 'report_untrusted', 'off')
62 self.ui.setconfig('ui', 'interactive', 'off')
62 self.ui.setconfig('ui', 'interactive', 'off')
63
63
64 if not isinstance(self.conf, (dict, list, tuple)):
64 if not isinstance(self.conf, (dict, list, tuple)):
65 map = {'paths': 'hgweb-paths'}
65 map = {'paths': 'hgweb-paths'}
66 self.ui.readconfig(self.conf, remap=map, trust=True)
66 self.ui.readconfig(self.conf, remap=map, trust=True)
67 paths = self.ui.configitems('hgweb-paths')
67 paths = self.ui.configitems('hgweb-paths')
68 elif isinstance(self.conf, (list, tuple)):
68 elif isinstance(self.conf, (list, tuple)):
69 paths = self.conf
69 paths = self.conf
70 elif isinstance(self.conf, dict):
70 elif isinstance(self.conf, dict):
71 paths = self.conf.items()
71 paths = self.conf.items()
72
72
73 encoding.encoding = self.ui.config('web', 'encoding',
73 encoding.encoding = self.ui.config('web', 'encoding',
74 encoding.encoding)
74 encoding.encoding)
75 self.motd = self.ui.config('web', 'motd')
75 self.motd = self.ui.config('web', 'motd')
76 self.style = self.ui.config('web', 'style', 'paper')
76 self.style = self.ui.config('web', 'style', 'paper')
77 self.stripecount = self.ui.config('web', 'stripes', 1)
77 self.stripecount = self.ui.config('web', 'stripes', 1)
78 if self.stripecount:
78 if self.stripecount:
79 self.stripecount = int(self.stripecount)
79 self.stripecount = int(self.stripecount)
80 self._baseurl = self.ui.config('web', 'baseurl')
80 self._baseurl = self.ui.config('web', 'baseurl')
81
81
82 self.repos = findrepos(paths)
82 self.repos = findrepos(paths)
83 for prefix, root in self.ui.configitems('collections'):
83 for prefix, root in self.ui.configitems('collections'):
84 prefix = util.pconvert(prefix)
84 prefix = util.pconvert(prefix)
85 for path in util.walkrepos(root, followsym=True):
85 for path in util.walkrepos(root, followsym=True):
86 repo = os.path.normpath(path)
86 repo = os.path.normpath(path)
87 name = util.pconvert(repo)
87 name = util.pconvert(repo)
88 if name.startswith(prefix):
88 if name.startswith(prefix):
89 name = name[len(prefix):]
89 name = name[len(prefix):]
90 self.repos.append((name.lstrip('/'), repo))
90 self.repos.append((name.lstrip('/'), repo))
91
91
92 self.repos.sort()
92 self.repos.sort()
93 self.lastrefresh = time.time()
93 self.lastrefresh = time.time()
94
94
95 def run(self):
95 def run(self):
96 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
96 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
97 raise RuntimeError("This function is only intended to be "
97 raise RuntimeError("This function is only intended to be "
98 "called while running as a CGI script.")
98 "called while running as a CGI script.")
99 import mercurial.hgweb.wsgicgi as wsgicgi
99 import mercurial.hgweb.wsgicgi as wsgicgi
100 wsgicgi.launch(self)
100 wsgicgi.launch(self)
101
101
102 def __call__(self, env, respond):
102 def __call__(self, env, respond):
103 req = wsgirequest(env, respond)
103 req = wsgirequest(env, respond)
104 return self.run_wsgi(req)
104 return self.run_wsgi(req)
105
105
106 def read_allowed(self, ui, req):
106 def read_allowed(self, ui, req):
107 """Check allow_read and deny_read config options of a repo's ui object
107 """Check allow_read and deny_read config options of a repo's ui object
108 to determine user permissions. By default, with neither option set (or
108 to determine user permissions. By default, with neither option set (or
109 both empty), allow all users to read the repo. There are two ways a
109 both empty), allow all users to read the repo. There are two ways a
110 user can be denied read access: (1) deny_read is not empty, and the
110 user can be denied read access: (1) deny_read is not empty, and the
111 user is unauthenticated or deny_read contains user (or *), and (2)
111 user is unauthenticated or deny_read contains user (or *), and (2)
112 allow_read is not empty and the user is not in allow_read. Return True
112 allow_read is not empty and the user is not in allow_read. Return True
113 if user is allowed to read the repo, else return False."""
113 if user is allowed to read the repo, else return False."""
114
114
115 user = req.env.get('REMOTE_USER')
115 user = req.env.get('REMOTE_USER')
116
116
117 deny_read = ui.configlist('web', 'deny_read', untrusted=True)
117 deny_read = ui.configlist('web', 'deny_read', untrusted=True)
118 if deny_read and (not user or deny_read == ['*'] or user in deny_read):
118 if deny_read and (not user or deny_read == ['*'] or user in deny_read):
119 return False
119 return False
120
120
121 allow_read = ui.configlist('web', 'allow_read', untrusted=True)
121 allow_read = ui.configlist('web', 'allow_read', untrusted=True)
122 # by default, allow reading if no allow_read option has been set
122 # by default, allow reading if no allow_read option has been set
123 if (not allow_read) or (allow_read == ['*']) or (user in allow_read):
123 if (not allow_read) or (allow_read == ['*']) or (user in allow_read):
124 return True
124 return True
125
125
126 return False
126 return False
127
127
128 def run_wsgi(self, req):
128 def run_wsgi(self, req):
129 try:
129 try:
130 try:
130 try:
131 self.refresh()
131 self.refresh()
132
132
133 virtual = req.env.get("PATH_INFO", "").strip('/')
133 virtual = req.env.get("PATH_INFO", "").strip('/')
134 tmpl = self.templater(req)
134 tmpl = self.templater(req)
135 ctype = tmpl('mimetype', encoding=encoding.encoding)
135 ctype = tmpl('mimetype', encoding=encoding.encoding)
136 ctype = templater.stringify(ctype)
136 ctype = templater.stringify(ctype)
137
137
138 # a static file
138 # a static file
139 if virtual.startswith('static/') or 'static' in req.form:
139 if virtual.startswith('static/') or 'static' in req.form:
140 if virtual.startswith('static/'):
140 if virtual.startswith('static/'):
141 fname = virtual[7:]
141 fname = virtual[7:]
142 else:
142 else:
143 fname = req.form['static'][0]
143 fname = req.form['static'][0]
144 static = templater.templatepath('static')
144 static = templater.templatepath('static')
145 return (staticfile(static, fname, req),)
145 return (staticfile(static, fname, req),)
146
146
147 # top-level index
147 # top-level index
148 elif not virtual:
148 elif not virtual:
149 req.respond(HTTP_OK, ctype)
149 req.respond(HTTP_OK, ctype)
150 return self.makeindex(req, tmpl)
150 return self.makeindex(req, tmpl)
151
151
152 # nested indexes and hgwebs
152 # nested indexes and hgwebs
153
153
154 repos = dict(self.repos)
154 repos = dict(self.repos)
155 while virtual:
155 while virtual:
156 real = repos.get(virtual)
156 real = repos.get(virtual)
157 if real:
157 if real:
158 req.env['REPO_NAME'] = virtual
158 req.env['REPO_NAME'] = virtual
159 try:
159 try:
160 repo = hg.repository(self.ui, real)
160 repo = hg.repository(self.ui, real)
161 return hgweb(repo).run_wsgi(req)
161 return hgweb(repo).run_wsgi(req)
162 except IOError, inst:
162 except IOError, inst:
163 msg = inst.strerror
163 msg = inst.strerror
164 raise ErrorResponse(HTTP_SERVER_ERROR, msg)
164 raise ErrorResponse(HTTP_SERVER_ERROR, msg)
165 except error.RepoError, inst:
165 except error.RepoError, inst:
166 raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
166 raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
167
167
168 # browse subdirectories
168 # browse subdirectories
169 subdir = virtual + '/'
169 subdir = virtual + '/'
170 if [r for r in repos if r.startswith(subdir)]:
170 if [r for r in repos if r.startswith(subdir)]:
171 req.respond(HTTP_OK, ctype)
171 req.respond(HTTP_OK, ctype)
172 return self.makeindex(req, tmpl, subdir)
172 return self.makeindex(req, tmpl, subdir)
173
173
174 up = virtual.rfind('/')
174 up = virtual.rfind('/')
175 if up < 0:
175 if up < 0:
176 break
176 break
177 virtual = virtual[:up]
177 virtual = virtual[:up]
178
178
179 # prefixes not found
179 # prefixes not found
180 req.respond(HTTP_NOT_FOUND, ctype)
180 req.respond(HTTP_NOT_FOUND, ctype)
181 return tmpl("notfound", repo=virtual)
181 return tmpl("notfound", repo=virtual)
182
182
183 except ErrorResponse, err:
183 except ErrorResponse, err:
184 req.respond(err, ctype)
184 req.respond(err, ctype)
185 return tmpl('error', error=err.message or '')
185 return tmpl('error', error=err.message or '')
186 finally:
186 finally:
187 tmpl = None
187 tmpl = None
188
188
189 def makeindex(self, req, tmpl, subdir=""):
189 def makeindex(self, req, tmpl, subdir=""):
190
190
191 def archivelist(ui, nodeid, url):
191 def archivelist(ui, nodeid, url):
192 allowed = ui.configlist("web", "allow_archive", untrusted=True)
192 allowed = ui.configlist("web", "allow_archive", untrusted=True)
193 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
193 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
194 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
194 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
195 untrusted=True):
195 untrusted=True):
196 yield {"type" : i[0], "extension": i[1],
196 yield {"type" : i[0], "extension": i[1],
197 "node": nodeid, "url": url}
197 "node": nodeid, "url": url}
198
198
199 sortdefault = 'name', False
199 sortdefault = 'name', False
200 def entries(sortcolumn="", descending=False, subdir="", **map):
200 def entries(sortcolumn="", descending=False, subdir="", **map):
201 rows = []
201 rows = []
202 parity = paritygen(self.stripecount)
202 parity = paritygen(self.stripecount)
203 for name, path in self.repos:
203 for name, path in self.repos:
204 if not name.startswith(subdir):
204 if not name.startswith(subdir):
205 continue
205 continue
206 name = name[len(subdir):]
206 name = name[len(subdir):]
207
207
208 u = self.ui.copy()
208 u = self.ui.copy()
209 try:
209 try:
210 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
210 u.readconfig(os.path.join(path, '.hg', 'hgrc'))
211 except Exception, e:
211 except Exception, e:
212 u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e))
212 u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e))
213 continue
213 continue
214 def get(section, name, default=None):
214 def get(section, name, default=None):
215 return u.config(section, name, default, untrusted=True)
215 return u.config(section, name, default, untrusted=True)
216
216
217 if u.configbool("web", "hidden", untrusted=True):
217 if u.configbool("web", "hidden", untrusted=True):
218 continue
218 continue
219
219
220 if not self.read_allowed(u, req):
220 if not self.read_allowed(u, req):
221 continue
221 continue
222
222
223 parts = [name]
223 parts = [name]
224 if 'PATH_INFO' in req.env:
224 if 'PATH_INFO' in req.env:
225 parts.insert(0, req.env['PATH_INFO'].rstrip('/'))
225 parts.insert(0, req.env['PATH_INFO'].rstrip('/'))
226 if req.env['SCRIPT_NAME']:
226 if req.env['SCRIPT_NAME']:
227 parts.insert(0, req.env['SCRIPT_NAME'])
227 parts.insert(0, req.env['SCRIPT_NAME'])
228 url = ('/'.join(parts).replace("//", "/")) + '/'
228 m = re.match('((?:https?://)?)(.*)', '/'.join(parts))
229 # squish repeated slashes out of the path component
230 url = m.group(1) + re.sub('/+', '/', m.group(2)) + '/'
229
231
230 # update time with local timezone
232 # update time with local timezone
231 try:
233 try:
232 d = (get_mtime(path), util.makedate()[1])
234 d = (get_mtime(path), util.makedate()[1])
233 except OSError:
235 except OSError:
234 continue
236 continue
235
237
236 contact = get_contact(get)
238 contact = get_contact(get)
237 description = get("web", "description", "")
239 description = get("web", "description", "")
238 name = get("web", "name", name)
240 name = get("web", "name", name)
239 row = dict(contact=contact or "unknown",
241 row = dict(contact=contact or "unknown",
240 contact_sort=contact.upper() or "unknown",
242 contact_sort=contact.upper() or "unknown",
241 name=name,
243 name=name,
242 name_sort=name,
244 name_sort=name,
243 url=url,
245 url=url,
244 description=description or "unknown",
246 description=description or "unknown",
245 description_sort=description.upper() or "unknown",
247 description_sort=description.upper() or "unknown",
246 lastchange=d,
248 lastchange=d,
247 lastchange_sort=d[1]-d[0],
249 lastchange_sort=d[1]-d[0],
248 archives=archivelist(u, "tip", url))
250 archives=archivelist(u, "tip", url))
249 if (not sortcolumn or (sortcolumn, descending) == sortdefault):
251 if (not sortcolumn or (sortcolumn, descending) == sortdefault):
250 # fast path for unsorted output
252 # fast path for unsorted output
251 row['parity'] = parity.next()
253 row['parity'] = parity.next()
252 yield row
254 yield row
253 else:
255 else:
254 rows.append((row["%s_sort" % sortcolumn], row))
256 rows.append((row["%s_sort" % sortcolumn], row))
255 if rows:
257 if rows:
256 rows.sort()
258 rows.sort()
257 if descending:
259 if descending:
258 rows.reverse()
260 rows.reverse()
259 for key, row in rows:
261 for key, row in rows:
260 row['parity'] = parity.next()
262 row['parity'] = parity.next()
261 yield row
263 yield row
262
264
263 self.refresh()
265 self.refresh()
264 sortable = ["name", "description", "contact", "lastchange"]
266 sortable = ["name", "description", "contact", "lastchange"]
265 sortcolumn, descending = sortdefault
267 sortcolumn, descending = sortdefault
266 if 'sort' in req.form:
268 if 'sort' in req.form:
267 sortcolumn = req.form['sort'][0]
269 sortcolumn = req.form['sort'][0]
268 descending = sortcolumn.startswith('-')
270 descending = sortcolumn.startswith('-')
269 if descending:
271 if descending:
270 sortcolumn = sortcolumn[1:]
272 sortcolumn = sortcolumn[1:]
271 if sortcolumn not in sortable:
273 if sortcolumn not in sortable:
272 sortcolumn = ""
274 sortcolumn = ""
273
275
274 sort = [("sort_%s" % column,
276 sort = [("sort_%s" % column,
275 "%s%s" % ((not descending and column == sortcolumn)
277 "%s%s" % ((not descending and column == sortcolumn)
276 and "-" or "", column))
278 and "-" or "", column))
277 for column in sortable]
279 for column in sortable]
278
280
279 self.refresh()
281 self.refresh()
280 if self._baseurl is not None:
282 if self._baseurl is not None:
281 req.env['SCRIPT_NAME'] = self._baseurl
283 req.env['SCRIPT_NAME'] = self._baseurl
282
284
283 return tmpl("index", entries=entries, subdir=subdir,
285 return tmpl("index", entries=entries, subdir=subdir,
284 sortcolumn=sortcolumn, descending=descending,
286 sortcolumn=sortcolumn, descending=descending,
285 **dict(sort))
287 **dict(sort))
286
288
287 def templater(self, req):
289 def templater(self, req):
288
290
289 def header(**map):
291 def header(**map):
290 yield tmpl('header', encoding=encoding.encoding, **map)
292 yield tmpl('header', encoding=encoding.encoding, **map)
291
293
292 def footer(**map):
294 def footer(**map):
293 yield tmpl("footer", **map)
295 yield tmpl("footer", **map)
294
296
295 def motd(**map):
297 def motd(**map):
296 if self.motd is not None:
298 if self.motd is not None:
297 yield self.motd
299 yield self.motd
298 else:
300 else:
299 yield config('web', 'motd', '')
301 yield config('web', 'motd', '')
300
302
301 def config(section, name, default=None, untrusted=True):
303 def config(section, name, default=None, untrusted=True):
302 return self.ui.config(section, name, default, untrusted)
304 return self.ui.config(section, name, default, untrusted)
303
305
304 if self._baseurl is not None:
306 if self._baseurl is not None:
305 req.env['SCRIPT_NAME'] = self._baseurl
307 req.env['SCRIPT_NAME'] = self._baseurl
306
308
307 url = req.env.get('SCRIPT_NAME', '')
309 url = req.env.get('SCRIPT_NAME', '')
308 if not url.endswith('/'):
310 if not url.endswith('/'):
309 url += '/'
311 url += '/'
310
312
311 vars = {}
313 vars = {}
312 style = self.style
314 style = self.style
313 if 'style' in req.form:
315 if 'style' in req.form:
314 vars['style'] = style = req.form['style'][0]
316 vars['style'] = style = req.form['style'][0]
315 start = url[-1] == '?' and '&' or '?'
317 start = url[-1] == '?' and '&' or '?'
316 sessionvars = webutil.sessionvars(vars, start)
318 sessionvars = webutil.sessionvars(vars, start)
317
319
318 staticurl = config('web', 'staticurl') or url + 'static/'
320 staticurl = config('web', 'staticurl') or url + 'static/'
319 if not staticurl.endswith('/'):
321 if not staticurl.endswith('/'):
320 staticurl += '/'
322 staticurl += '/'
321
323
322 style = 'style' in req.form and req.form['style'][0] or self.style
324 style = 'style' in req.form and req.form['style'][0] or self.style
323 mapfile = templater.stylemap(style)
325 mapfile = templater.stylemap(style)
324 tmpl = templater.templater(mapfile,
326 tmpl = templater.templater(mapfile,
325 defaults={"header": header,
327 defaults={"header": header,
326 "footer": footer,
328 "footer": footer,
327 "motd": motd,
329 "motd": motd,
328 "url": url,
330 "url": url,
329 "staticurl": staticurl,
331 "staticurl": staticurl,
330 "sessionvars": sessionvars})
332 "sessionvars": sessionvars})
331 return tmpl
333 return tmpl
@@ -1,106 +1,107 b''
1 #!/bin/sh
1 #!/bin/sh
2 # Tests some basic hgwebdir functionality. Tests setting up paths and
2 # Tests some basic hgwebdir functionality. Tests setting up paths and
3 # collection, different forms of 404s and the subdirectory support.
3 # collection, different forms of 404s and the subdirectory support.
4
4
5 mkdir webdir
5 mkdir webdir
6 cd webdir
6 cd webdir
7
7
8 hg init a
8 hg init a
9 echo a > a/a
9 echo a > a/a
10 hg --cwd a ci -Ama -d'1 0'
10 hg --cwd a ci -Ama -d'1 0'
11 # create a mercurial queue repository
11 # create a mercurial queue repository
12 hg --cwd a qinit --config extensions.hgext.mq= -c
12 hg --cwd a qinit --config extensions.hgext.mq= -c
13
13
14 hg init b
14 hg init b
15 echo b > b/b
15 echo b > b/b
16 hg --cwd b ci -Amb -d'2 0'
16 hg --cwd b ci -Amb -d'2 0'
17
17
18 # create a nested repository
18 # create a nested repository
19 cd b
19 cd b
20 hg init d
20 hg init d
21 echo d > d/d
21 echo d > d/d
22 hg --cwd d ci -Amd -d'3 0'
22 hg --cwd d ci -Amd -d'3 0'
23 cd ..
23 cd ..
24
24
25 hg init c
25 hg init c
26 echo c > c/c
26 echo c > c/c
27 hg --cwd c ci -Amc -d'3 0'
27 hg --cwd c ci -Amc -d'3 0'
28
28
29 root=`pwd`
29 root=`pwd`
30 cd ..
30 cd ..
31
31
32 cat > paths.conf <<EOF
32 cat > paths.conf <<EOF
33 [paths]
33 [paths]
34 a=$root/a
34 a=$root/a
35 b=$root/b
35 b=$root/b
36 EOF
36 EOF
37
37
38 hg serve -p $HGPORT -d --pid-file=hg.pid --webdir-conf paths.conf \
38 hg serve -p $HGPORT -d --pid-file=hg.pid --webdir-conf paths.conf \
39 -A access-paths.log -E error-paths-1.log
39 -A access-paths.log -E error-paths-1.log
40 cat hg.pid >> $DAEMON_PIDS
40 cat hg.pid >> $DAEMON_PIDS
41
41
42 echo % should give a 404 - file does not exist
42 echo % should give a 404 - file does not exist
43 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/a/file/tip/bork?style=raw'
43 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/a/file/tip/bork?style=raw'
44
44
45 echo % should succeed
45 echo % should succeed
46 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/?style=raw'
46 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/?style=raw'
47 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/a/file/tip/a?style=raw'
47 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/a/file/tip/a?style=raw'
48 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/b/file/tip/b?style=raw'
48 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/b/file/tip/b?style=raw'
49
49
50 echo % should give a 404 - repo is not published
50 echo % should give a 404 - repo is not published
51 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/c/file/tip/c?style=raw'
51 "$TESTDIR/get-with-headers.py" localhost:$HGPORT '/c/file/tip/c?style=raw'
52
52
53 cat > paths.conf <<EOF
53 cat > paths.conf <<EOF
54 [paths]
54 [paths]
55 t/a/=$root/a
55 t/a/=$root/a
56 b=$root/b
56 b=$root/b
57 coll=$root/*
57 coll=$root/*
58 rcoll=$root/**
58 rcoll=$root/**
59 EOF
59 EOF
60
60
61 hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
61 hg serve -p $HGPORT1 -d --pid-file=hg.pid --webdir-conf paths.conf \
62 -A access-paths.log -E error-paths-2.log
62 -A access-paths.log -E error-paths-2.log
63 cat hg.pid >> $DAEMON_PIDS
63 cat hg.pid >> $DAEMON_PIDS
64
64
65 echo % should succeed, slashy names
65 echo % should succeed, slashy names
66 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/?style=raw'
66 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/?style=raw'
67 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/?style=paper' \
67 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/?style=paper' \
68 | sed "s/[0-9]\{1,\} seconds\{0,1\} ago/seconds ago/"
68 | sed "s/[0-9]\{1,\} seconds\{0,1\} ago/seconds ago/"
69 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t?style=raw'
69 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t?style=raw'
70 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/?style=raw'
70 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/?style=raw'
71 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/?style=paper' \
71 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/?style=paper' \
72 | sed "s/[0-9]\{1,\} seconds\{0,1\} ago/seconds ago/"
72 | sed "s/[0-9]\{1,\} seconds\{0,1\} ago/seconds ago/"
73 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a?style=atom' \
73 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a?style=atom' \
74 | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//"
74 | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//"
75 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a/?style=atom' \
75 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a/?style=atom' \
76 | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//"
76 | sed "s/http:\/\/[^/]*\//http:\/\/127.0.0.1\//"
77 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a/file/tip/a?style=raw'
77 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/t/a/file/tip/a?style=raw'
78 # Test [paths] '*' extension
78 # Test [paths] '*' extension
79 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/coll/?style=raw'
79 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/coll/?style=raw'
80 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/coll/a/file/tip/a?style=raw'
80 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/coll/a/file/tip/a?style=raw'
81 #test [paths] '**' extension
81 #test [paths] '**' extension
82 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/rcoll/?style=raw'
82 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/rcoll/?style=raw'
83 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/rcoll/b/d/file/tip/d?style=raw'
83 "$TESTDIR/get-with-headers.py" localhost:$HGPORT1 '/rcoll/b/d/file/tip/d?style=raw'
84
84
85
85
86 cat > collections.conf <<EOF
86 cat > collections.conf <<EOF
87 [collections]
87 [collections]
88 $root=$root
88 $root=$root
89 EOF
89 EOF
90
90
91 hg serve -p $HGPORT2 -d --pid-file=hg.pid --webdir-conf collections.conf \
91 hg serve --config web.baseurl=http://hg.example.com:8080/ -p $HGPORT2 -d \
92 --pid-file=hg.pid --webdir-conf collections.conf \
92 -A access-collections.log -E error-collections.log
93 -A access-collections.log -E error-collections.log
93 cat hg.pid >> $DAEMON_PIDS
94 cat hg.pid >> $DAEMON_PIDS
94
95
95 echo % collections: should succeed
96 echo % collections: should succeed
96 "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/?style=raw'
97 "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/?style=raw'
97 "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/a/file/tip/a?style=raw'
98 "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/a/file/tip/a?style=raw'
98 "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/b/file/tip/b?style=raw'
99 "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/b/file/tip/b?style=raw'
99 "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/c/file/tip/c?style=raw'
100 "$TESTDIR/get-with-headers.py" localhost:$HGPORT2 '/c/file/tip/c?style=raw'
100
101
101 echo % paths errors 1
102 echo % paths errors 1
102 cat error-paths-1.log
103 cat error-paths-1.log
103 echo % paths errors 2
104 echo % paths errors 2
104 cat error-paths-2.log
105 cat error-paths-2.log
105 echo % collections errors
106 echo % collections errors
106 cat error-collections.log
107 cat error-collections.log
@@ -1,330 +1,330 b''
1 adding a
1 adding a
2 adding b
2 adding b
3 adding d
3 adding d
4 adding c
4 adding c
5 % should give a 404 - file does not exist
5 % should give a 404 - file does not exist
6 404 Not Found
6 404 Not Found
7
7
8
8
9 error: bork@8580ff50825a: not found in manifest
9 error: bork@8580ff50825a: not found in manifest
10 % should succeed
10 % should succeed
11 200 Script output follows
11 200 Script output follows
12
12
13
13
14 /a/
14 /a/
15 /b/
15 /b/
16
16
17 200 Script output follows
17 200 Script output follows
18
18
19 a
19 a
20 200 Script output follows
20 200 Script output follows
21
21
22 b
22 b
23 % should give a 404 - repo is not published
23 % should give a 404 - repo is not published
24 404 Not Found
24 404 Not Found
25
25
26
26
27 error: repository c not found
27 error: repository c not found
28 % should succeed, slashy names
28 % should succeed, slashy names
29 200 Script output follows
29 200 Script output follows
30
30
31
31
32 /b/
32 /b/
33 /coll/a/
33 /coll/a/
34 /coll/a/.hg/patches/
34 /coll/a/.hg/patches/
35 /coll/b/
35 /coll/b/
36 /coll/c/
36 /coll/c/
37 /rcoll/a/
37 /rcoll/a/
38 /rcoll/a/.hg/patches/
38 /rcoll/a/.hg/patches/
39 /rcoll/b/
39 /rcoll/b/
40 /rcoll/b/d/
40 /rcoll/b/d/
41 /rcoll/c/
41 /rcoll/c/
42 /t/a/
42 /t/a/
43
43
44 200 Script output follows
44 200 Script output follows
45
45
46 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
46 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
47 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
47 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
48 <head>
48 <head>
49 <link rel="icon" href="/static/hgicon.png" type="image/png" />
49 <link rel="icon" href="/static/hgicon.png" type="image/png" />
50 <meta name="robots" content="index, nofollow" />
50 <meta name="robots" content="index, nofollow" />
51 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
51 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
52
52
53 <title>Mercurial repositories index</title>
53 <title>Mercurial repositories index</title>
54 </head>
54 </head>
55 <body>
55 <body>
56
56
57 <div class="container">
57 <div class="container">
58 <div class="menu">
58 <div class="menu">
59 <a href="http://mercurial.selenic.com/">
59 <a href="http://mercurial.selenic.com/">
60 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
60 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
61 </div>
61 </div>
62 <div class="main">
62 <div class="main">
63 <h2>Mercurial Repositories</h2>
63 <h2>Mercurial Repositories</h2>
64
64
65 <table class="bigtable">
65 <table class="bigtable">
66 <tr>
66 <tr>
67 <th><a href="?sort=-name">Name</a></th>
67 <th><a href="?sort=-name">Name</a></th>
68 <th><a href="?sort=description">Description</a></th>
68 <th><a href="?sort=description">Description</a></th>
69 <th><a href="?sort=contact">Contact</a></th>
69 <th><a href="?sort=contact">Contact</a></th>
70 <th><a href="?sort=lastchange">Last change</a></th>
70 <th><a href="?sort=lastchange">Last change</a></th>
71 <th>&nbsp;</th>
71 <th>&nbsp;</th>
72 </tr>
72 </tr>
73
73
74 <tr class="parity0">
74 <tr class="parity0">
75 <td><a href="/b/?style=paper">b</a></td>
75 <td><a href="/b/?style=paper">b</a></td>
76 <td>unknown</td>
76 <td>unknown</td>
77 <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>
77 <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>
78 <td class="age">seconds ago</td>
78 <td class="age">seconds ago</td>
79 <td class="indexlinks"></td>
79 <td class="indexlinks"></td>
80 </tr>
80 </tr>
81
81
82 <tr class="parity1">
82 <tr class="parity1">
83 <td><a href="/coll/a/?style=paper">coll/a</a></td>
83 <td><a href="/coll/a/?style=paper">coll/a</a></td>
84 <td>unknown</td>
84 <td>unknown</td>
85 <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>
85 <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>
86 <td class="age">seconds ago</td>
86 <td class="age">seconds ago</td>
87 <td class="indexlinks"></td>
87 <td class="indexlinks"></td>
88 </tr>
88 </tr>
89
89
90 <tr class="parity0">
90 <tr class="parity0">
91 <td><a href="/coll/a/.hg/patches/?style=paper">coll/a/.hg/patches</a></td>
91 <td><a href="/coll/a/.hg/patches/?style=paper">coll/a/.hg/patches</a></td>
92 <td>unknown</td>
92 <td>unknown</td>
93 <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>
93 <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>
94 <td class="age">seconds ago</td>
94 <td class="age">seconds ago</td>
95 <td class="indexlinks"></td>
95 <td class="indexlinks"></td>
96 </tr>
96 </tr>
97
97
98 <tr class="parity1">
98 <tr class="parity1">
99 <td><a href="/coll/b/?style=paper">coll/b</a></td>
99 <td><a href="/coll/b/?style=paper">coll/b</a></td>
100 <td>unknown</td>
100 <td>unknown</td>
101 <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>
101 <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>
102 <td class="age">seconds ago</td>
102 <td class="age">seconds ago</td>
103 <td class="indexlinks"></td>
103 <td class="indexlinks"></td>
104 </tr>
104 </tr>
105
105
106 <tr class="parity0">
106 <tr class="parity0">
107 <td><a href="/coll/c/?style=paper">coll/c</a></td>
107 <td><a href="/coll/c/?style=paper">coll/c</a></td>
108 <td>unknown</td>
108 <td>unknown</td>
109 <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>
109 <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>
110 <td class="age">seconds ago</td>
110 <td class="age">seconds ago</td>
111 <td class="indexlinks"></td>
111 <td class="indexlinks"></td>
112 </tr>
112 </tr>
113
113
114 <tr class="parity1">
114 <tr class="parity1">
115 <td><a href="/rcoll/a/?style=paper">rcoll/a</a></td>
115 <td><a href="/rcoll/a/?style=paper">rcoll/a</a></td>
116 <td>unknown</td>
116 <td>unknown</td>
117 <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>
117 <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>
118 <td class="age">seconds ago</td>
118 <td class="age">seconds ago</td>
119 <td class="indexlinks"></td>
119 <td class="indexlinks"></td>
120 </tr>
120 </tr>
121
121
122 <tr class="parity0">
122 <tr class="parity0">
123 <td><a href="/rcoll/a/.hg/patches/?style=paper">rcoll/a/.hg/patches</a></td>
123 <td><a href="/rcoll/a/.hg/patches/?style=paper">rcoll/a/.hg/patches</a></td>
124 <td>unknown</td>
124 <td>unknown</td>
125 <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>
125 <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>
126 <td class="age">seconds ago</td>
126 <td class="age">seconds ago</td>
127 <td class="indexlinks"></td>
127 <td class="indexlinks"></td>
128 </tr>
128 </tr>
129
129
130 <tr class="parity1">
130 <tr class="parity1">
131 <td><a href="/rcoll/b/?style=paper">rcoll/b</a></td>
131 <td><a href="/rcoll/b/?style=paper">rcoll/b</a></td>
132 <td>unknown</td>
132 <td>unknown</td>
133 <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>
133 <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>
134 <td class="age">seconds ago</td>
134 <td class="age">seconds ago</td>
135 <td class="indexlinks"></td>
135 <td class="indexlinks"></td>
136 </tr>
136 </tr>
137
137
138 <tr class="parity0">
138 <tr class="parity0">
139 <td><a href="/rcoll/b/d/?style=paper">rcoll/b/d</a></td>
139 <td><a href="/rcoll/b/d/?style=paper">rcoll/b/d</a></td>
140 <td>unknown</td>
140 <td>unknown</td>
141 <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>
141 <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>
142 <td class="age">seconds ago</td>
142 <td class="age">seconds ago</td>
143 <td class="indexlinks"></td>
143 <td class="indexlinks"></td>
144 </tr>
144 </tr>
145
145
146 <tr class="parity1">
146 <tr class="parity1">
147 <td><a href="/rcoll/c/?style=paper">rcoll/c</a></td>
147 <td><a href="/rcoll/c/?style=paper">rcoll/c</a></td>
148 <td>unknown</td>
148 <td>unknown</td>
149 <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>
149 <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>
150 <td class="age">seconds ago</td>
150 <td class="age">seconds ago</td>
151 <td class="indexlinks"></td>
151 <td class="indexlinks"></td>
152 </tr>
152 </tr>
153
153
154 <tr class="parity0">
154 <tr class="parity0">
155 <td><a href="/t/a/?style=paper">t/a</a></td>
155 <td><a href="/t/a/?style=paper">t/a</a></td>
156 <td>unknown</td>
156 <td>unknown</td>
157 <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>
157 <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>
158 <td class="age">seconds ago</td>
158 <td class="age">seconds ago</td>
159 <td class="indexlinks"></td>
159 <td class="indexlinks"></td>
160 </tr>
160 </tr>
161
161
162 </table>
162 </table>
163 </div>
163 </div>
164 </div>
164 </div>
165
165
166
166
167 </body>
167 </body>
168 </html>
168 </html>
169
169
170 200 Script output follows
170 200 Script output follows
171
171
172
172
173 /t/a/
173 /t/a/
174
174
175 200 Script output follows
175 200 Script output follows
176
176
177
177
178 /t/a/
178 /t/a/
179
179
180 200 Script output follows
180 200 Script output follows
181
181
182 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
182 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
183 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
183 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
184 <head>
184 <head>
185 <link rel="icon" href="/static/hgicon.png" type="image/png" />
185 <link rel="icon" href="/static/hgicon.png" type="image/png" />
186 <meta name="robots" content="index, nofollow" />
186 <meta name="robots" content="index, nofollow" />
187 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
187 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
188
188
189 <title>Mercurial repositories index</title>
189 <title>Mercurial repositories index</title>
190 </head>
190 </head>
191 <body>
191 <body>
192
192
193 <div class="container">
193 <div class="container">
194 <div class="menu">
194 <div class="menu">
195 <a href="http://mercurial.selenic.com/">
195 <a href="http://mercurial.selenic.com/">
196 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
196 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
197 </div>
197 </div>
198 <div class="main">
198 <div class="main">
199 <h2>Mercurial Repositories</h2>
199 <h2>Mercurial Repositories</h2>
200
200
201 <table class="bigtable">
201 <table class="bigtable">
202 <tr>
202 <tr>
203 <th><a href="?sort=-name">Name</a></th>
203 <th><a href="?sort=-name">Name</a></th>
204 <th><a href="?sort=description">Description</a></th>
204 <th><a href="?sort=description">Description</a></th>
205 <th><a href="?sort=contact">Contact</a></th>
205 <th><a href="?sort=contact">Contact</a></th>
206 <th><a href="?sort=lastchange">Last change</a></th>
206 <th><a href="?sort=lastchange">Last change</a></th>
207 <th>&nbsp;</th>
207 <th>&nbsp;</th>
208 </tr>
208 </tr>
209
209
210 <tr class="parity0">
210 <tr class="parity0">
211 <td><a href="/t/a/?style=paper">a</a></td>
211 <td><a href="/t/a/?style=paper">a</a></td>
212 <td>unknown</td>
212 <td>unknown</td>
213 <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>
213 <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>
214 <td class="age">seconds ago</td>
214 <td class="age">seconds ago</td>
215 <td class="indexlinks"></td>
215 <td class="indexlinks"></td>
216 </tr>
216 </tr>
217
217
218 </table>
218 </table>
219 </div>
219 </div>
220 </div>
220 </div>
221
221
222
222
223 </body>
223 </body>
224 </html>
224 </html>
225
225
226 200 Script output follows
226 200 Script output follows
227
227
228 <?xml version="1.0" encoding="ascii"?>
228 <?xml version="1.0" encoding="ascii"?>
229 <feed xmlns="http://127.0.0.1/2005/Atom">
229 <feed xmlns="http://127.0.0.1/2005/Atom">
230 <!-- Changelog -->
230 <!-- Changelog -->
231 <id>http://127.0.0.1/t/a/</id>
231 <id>http://127.0.0.1/t/a/</id>
232 <link rel="self" href="http://127.0.0.1/t/a/atom-log"/>
232 <link rel="self" href="http://127.0.0.1/t/a/atom-log"/>
233 <link rel="alternate" href="http://127.0.0.1/t/a/"/>
233 <link rel="alternate" href="http://127.0.0.1/t/a/"/>
234 <title>t/a Changelog</title>
234 <title>t/a Changelog</title>
235 <updated>1970-01-01T00:00:01+00:00</updated>
235 <updated>1970-01-01T00:00:01+00:00</updated>
236
236
237 <entry>
237 <entry>
238 <title>a</title>
238 <title>a</title>
239 <id>http://127.0.0.1/t/a/#changeset-8580ff50825a50c8f716709acdf8de0deddcd6ab</id>
239 <id>http://127.0.0.1/t/a/#changeset-8580ff50825a50c8f716709acdf8de0deddcd6ab</id>
240 <link href="http://127.0.0.1/t/a/rev/8580ff50825a50c8f716709acdf8de0deddcd6ab"/>
240 <link href="http://127.0.0.1/t/a/rev/8580ff50825a50c8f716709acdf8de0deddcd6ab"/>
241 <author>
241 <author>
242 <name>test</name>
242 <name>test</name>
243 <email>&#116;&#101;&#115;&#116;</email>
243 <email>&#116;&#101;&#115;&#116;</email>
244 </author>
244 </author>
245 <updated>1970-01-01T00:00:01+00:00</updated>
245 <updated>1970-01-01T00:00:01+00:00</updated>
246 <published>1970-01-01T00:00:01+00:00</published>
246 <published>1970-01-01T00:00:01+00:00</published>
247 <content type="xhtml">
247 <content type="xhtml">
248 <div xmlns="http://127.0.0.1/1999/xhtml">
248 <div xmlns="http://127.0.0.1/1999/xhtml">
249 <pre xml:space="preserve">a</pre>
249 <pre xml:space="preserve">a</pre>
250 </div>
250 </div>
251 </content>
251 </content>
252 </entry>
252 </entry>
253
253
254 </feed>
254 </feed>
255 200 Script output follows
255 200 Script output follows
256
256
257 <?xml version="1.0" encoding="ascii"?>
257 <?xml version="1.0" encoding="ascii"?>
258 <feed xmlns="http://127.0.0.1/2005/Atom">
258 <feed xmlns="http://127.0.0.1/2005/Atom">
259 <!-- Changelog -->
259 <!-- Changelog -->
260 <id>http://127.0.0.1/t/a/</id>
260 <id>http://127.0.0.1/t/a/</id>
261 <link rel="self" href="http://127.0.0.1/t/a/atom-log"/>
261 <link rel="self" href="http://127.0.0.1/t/a/atom-log"/>
262 <link rel="alternate" href="http://127.0.0.1/t/a/"/>
262 <link rel="alternate" href="http://127.0.0.1/t/a/"/>
263 <title>t/a Changelog</title>
263 <title>t/a Changelog</title>
264 <updated>1970-01-01T00:00:01+00:00</updated>
264 <updated>1970-01-01T00:00:01+00:00</updated>
265
265
266 <entry>
266 <entry>
267 <title>a</title>
267 <title>a</title>
268 <id>http://127.0.0.1/t/a/#changeset-8580ff50825a50c8f716709acdf8de0deddcd6ab</id>
268 <id>http://127.0.0.1/t/a/#changeset-8580ff50825a50c8f716709acdf8de0deddcd6ab</id>
269 <link href="http://127.0.0.1/t/a/rev/8580ff50825a50c8f716709acdf8de0deddcd6ab"/>
269 <link href="http://127.0.0.1/t/a/rev/8580ff50825a50c8f716709acdf8de0deddcd6ab"/>
270 <author>
270 <author>
271 <name>test</name>
271 <name>test</name>
272 <email>&#116;&#101;&#115;&#116;</email>
272 <email>&#116;&#101;&#115;&#116;</email>
273 </author>
273 </author>
274 <updated>1970-01-01T00:00:01+00:00</updated>
274 <updated>1970-01-01T00:00:01+00:00</updated>
275 <published>1970-01-01T00:00:01+00:00</published>
275 <published>1970-01-01T00:00:01+00:00</published>
276 <content type="xhtml">
276 <content type="xhtml">
277 <div xmlns="http://127.0.0.1/1999/xhtml">
277 <div xmlns="http://127.0.0.1/1999/xhtml">
278 <pre xml:space="preserve">a</pre>
278 <pre xml:space="preserve">a</pre>
279 </div>
279 </div>
280 </content>
280 </content>
281 </entry>
281 </entry>
282
282
283 </feed>
283 </feed>
284 200 Script output follows
284 200 Script output follows
285
285
286 a
286 a
287 200 Script output follows
287 200 Script output follows
288
288
289
289
290 /coll/a/
290 /coll/a/
291 /coll/a/.hg/patches/
291 /coll/a/.hg/patches/
292 /coll/b/
292 /coll/b/
293 /coll/c/
293 /coll/c/
294
294
295 200 Script output follows
295 200 Script output follows
296
296
297 a
297 a
298 200 Script output follows
298 200 Script output follows
299
299
300
300
301 /rcoll/a/
301 /rcoll/a/
302 /rcoll/a/.hg/patches/
302 /rcoll/a/.hg/patches/
303 /rcoll/b/
303 /rcoll/b/
304 /rcoll/b/d/
304 /rcoll/b/d/
305 /rcoll/c/
305 /rcoll/c/
306
306
307 200 Script output follows
307 200 Script output follows
308
308
309 d
309 d
310 % collections: should succeed
310 % collections: should succeed
311 200 Script output follows
311 200 Script output follows
312
312
313
313
314 /a/
314 http://hg.example.com:8080/a/
315 /a/.hg/patches/
315 http://hg.example.com:8080/a/.hg/patches/
316 /b/
316 http://hg.example.com:8080/b/
317 /c/
317 http://hg.example.com:8080/c/
318
318
319 200 Script output follows
319 200 Script output follows
320
320
321 a
321 a
322 200 Script output follows
322 200 Script output follows
323
323
324 b
324 b
325 200 Script output follows
325 200 Script output follows
326
326
327 c
327 c
328 % paths errors 1
328 % paths errors 1
329 % paths errors 2
329 % paths errors 2
330 % collections errors
330 % collections errors
General Comments 0
You need to be logged in to leave comments. Login now