##// END OF EJS Templates
wireproto: allow unbundle with hashed heads parameter (issue2126)...
Shuhei Takahashi -
r13942:88f0e41d default
parent child Browse files
Show More
@@ -0,0 +1,31 b''
1
2 Test wire protocol unbundle with hashed heads (capability: unbundlehash)
3
4 Create a remote repository.
5
6 $ hg init remote
7 $ hg serve -R remote --config web.push_ssl=False --config web.allow_push=* -p $HGPORT -d --pid-file=hg1.pid -E error.log -A access.log
8 $ cat hg1.pid >> $DAEMON_PIDS
9
10 Clone the repository and push a change.
11
12 $ hg clone http://localhost:$HGPORT/ local
13 no changes found
14 updating to branch default
15 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
16 $ touch local/README
17 $ hg ci -R local -A -m hoge
18 adding README
19 $ hg push -R local
20 pushing to http://localhost:$HGPORT/
21 searching for changes
22 remote: adding changesets
23 remote: adding manifests
24 remote: adding file changes
25 remote: added 1 changesets with 1 changes to 1 files
26
27 Ensure hashed heads format is used.
28 The hash here is always the same since the remote repository only has the null head.
29
30 $ cat access.log | grep unbundle
31 * - - [*] "POST /?cmd=unbundle&heads=686173686564+6768033e216468247bd031a0a2d9876d79818f8f HTTP/1.1" 200 - (glob)
@@ -1,411 +1,420 b''
1 1 # wireproto.py - generic wire protocol support functions
2 2 #
3 3 # Copyright 2005-2010 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 import urllib, tempfile, os, sys
9 9 from i18n import _
10 10 from node import bin, hex
11 11 import changegroup as changegroupmod
12 12 import repo, error, encoding, util, store
13 13 import pushkey as pushkeymod
14 14
15 15 # list of nodes encoding / decoding
16 16
17 17 def decodelist(l, sep=' '):
18 18 if l:
19 19 return map(bin, l.split(sep))
20 20 return []
21 21
22 22 def encodelist(l, sep=' '):
23 23 return sep.join(map(hex, l))
24 24
25 25 # client side
26 26
27 27 class wirerepository(repo.repository):
28 28 def lookup(self, key):
29 29 self.requirecap('lookup', _('look up remote revision'))
30 30 d = self._call("lookup", key=encoding.fromlocal(key))
31 31 success, data = d[:-1].split(" ", 1)
32 32 if int(success):
33 33 return bin(data)
34 34 self._abort(error.RepoError(data))
35 35
36 36 def heads(self):
37 37 d = self._call("heads")
38 38 try:
39 39 return decodelist(d[:-1])
40 40 except ValueError:
41 41 self._abort(error.ResponseError(_("unexpected response:"), d))
42 42
43 43 def known(self, nodes):
44 44 n = encodelist(nodes)
45 45 d = self._call("known", nodes=n)
46 46 try:
47 47 return [bool(int(f)) for f in d]
48 48 except ValueError:
49 49 self._abort(error.ResponseError(_("unexpected response:"), d))
50 50
51 51 def branchmap(self):
52 52 d = self._call("branchmap")
53 53 try:
54 54 branchmap = {}
55 55 for branchpart in d.splitlines():
56 56 branchname, branchheads = branchpart.split(' ', 1)
57 57 branchname = encoding.tolocal(urllib.unquote(branchname))
58 58 branchheads = decodelist(branchheads)
59 59 branchmap[branchname] = branchheads
60 60 return branchmap
61 61 except TypeError:
62 62 self._abort(error.ResponseError(_("unexpected response:"), d))
63 63
64 64 def branches(self, nodes):
65 65 n = encodelist(nodes)
66 66 d = self._call("branches", nodes=n)
67 67 try:
68 68 br = [tuple(decodelist(b)) for b in d.splitlines()]
69 69 return br
70 70 except ValueError:
71 71 self._abort(error.ResponseError(_("unexpected response:"), d))
72 72
73 73 def between(self, pairs):
74 74 batch = 8 # avoid giant requests
75 75 r = []
76 76 for i in xrange(0, len(pairs), batch):
77 77 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
78 78 d = self._call("between", pairs=n)
79 79 try:
80 80 r.extend(l and decodelist(l) or [] for l in d.splitlines())
81 81 except ValueError:
82 82 self._abort(error.ResponseError(_("unexpected response:"), d))
83 83 return r
84 84
85 85 def pushkey(self, namespace, key, old, new):
86 86 if not self.capable('pushkey'):
87 87 return False
88 88 d = self._call("pushkey",
89 89 namespace=encoding.fromlocal(namespace),
90 90 key=encoding.fromlocal(key),
91 91 old=encoding.fromlocal(old),
92 92 new=encoding.fromlocal(new))
93 93 try:
94 94 d = bool(int(d))
95 95 except ValueError:
96 96 raise error.ResponseError(
97 97 _('push failed (unexpected response):'), d)
98 98 return d
99 99
100 100 def listkeys(self, namespace):
101 101 if not self.capable('pushkey'):
102 102 return {}
103 103 d = self._call("listkeys", namespace=encoding.fromlocal(namespace))
104 104 r = {}
105 105 for l in d.splitlines():
106 106 k, v = l.split('\t')
107 107 r[encoding.tolocal(k)] = encoding.tolocal(v)
108 108 return r
109 109
110 110 def stream_out(self):
111 111 return self._callstream('stream_out')
112 112
113 113 def changegroup(self, nodes, kind):
114 114 n = encodelist(nodes)
115 115 f = self._callstream("changegroup", roots=n)
116 116 return changegroupmod.unbundle10(self._decompress(f), 'UN')
117 117
118 118 def changegroupsubset(self, bases, heads, kind):
119 119 self.requirecap('changegroupsubset', _('look up remote changes'))
120 120 bases = encodelist(bases)
121 121 heads = encodelist(heads)
122 122 f = self._callstream("changegroupsubset",
123 123 bases=bases, heads=heads)
124 124 return changegroupmod.unbundle10(self._decompress(f), 'UN')
125 125
126 126 def getbundle(self, source, heads=None, common=None):
127 127 self.requirecap('getbundle', _('look up remote changes'))
128 128 opts = {}
129 129 if heads is not None:
130 130 opts['heads'] = encodelist(heads)
131 131 if common is not None:
132 132 opts['common'] = encodelist(common)
133 133 f = self._callstream("getbundle", **opts)
134 134 return changegroupmod.unbundle10(self._decompress(f), 'UN')
135 135
136 136 def unbundle(self, cg, heads, source):
137 137 '''Send cg (a readable file-like object representing the
138 138 changegroup to push, typically a chunkbuffer object) to the
139 139 remote server as a bundle. Return an integer indicating the
140 140 result of the push (see localrepository.addchangegroup()).'''
141 141
142 ret, output = self._callpush("unbundle", cg, heads=encodelist(heads))
142 if self.capable('unbundlehash'):
143 heads = encodelist(['hashed',
144 util.sha1(''.join(sorted(heads))).digest()])
145 else:
146 heads = encodelist(heads)
147
148 ret, output = self._callpush("unbundle", cg, heads=heads)
143 149 if ret == "":
144 150 raise error.ResponseError(
145 151 _('push failed:'), output)
146 152 try:
147 153 ret = int(ret)
148 154 except ValueError:
149 155 raise error.ResponseError(
150 156 _('push failed (unexpected response):'), ret)
151 157
152 158 for l in output.splitlines(True):
153 159 self.ui.status(_('remote: '), l)
154 160 return ret
155 161
156 162 def debugwireargs(self, one, two, three=None, four=None):
157 163 # don't pass optional arguments left at their default value
158 164 opts = {}
159 165 if three is not None:
160 166 opts['three'] = three
161 167 if four is not None:
162 168 opts['four'] = four
163 169 return self._call('debugwireargs', one=one, two=two, **opts)
164 170
165 171 # server side
166 172
167 173 class streamres(object):
168 174 def __init__(self, gen):
169 175 self.gen = gen
170 176
171 177 class pushres(object):
172 178 def __init__(self, res):
173 179 self.res = res
174 180
175 181 class pusherr(object):
176 182 def __init__(self, res):
177 183 self.res = res
178 184
179 185 def dispatch(repo, proto, command):
180 186 func, spec = commands[command]
181 187 args = proto.getargs(spec)
182 188 return func(repo, proto, *args)
183 189
184 190 def options(cmd, keys, others):
185 191 opts = {}
186 192 for k in keys:
187 193 if k in others:
188 194 opts[k] = others[k]
189 195 del others[k]
190 196 if others:
191 197 sys.stderr.write("abort: %s got unexpected arguments %s\n"
192 198 % (cmd, ",".join(others)))
193 199 return opts
194 200
195 201 def between(repo, proto, pairs):
196 202 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
197 203 r = []
198 204 for b in repo.between(pairs):
199 205 r.append(encodelist(b) + "\n")
200 206 return "".join(r)
201 207
202 208 def branchmap(repo, proto):
203 209 branchmap = repo.branchmap()
204 210 heads = []
205 211 for branch, nodes in branchmap.iteritems():
206 212 branchname = urllib.quote(encoding.fromlocal(branch))
207 213 branchnodes = encodelist(nodes)
208 214 heads.append('%s %s' % (branchname, branchnodes))
209 215 return '\n'.join(heads)
210 216
211 217 def branches(repo, proto, nodes):
212 218 nodes = decodelist(nodes)
213 219 r = []
214 220 for b in repo.branches(nodes):
215 221 r.append(encodelist(b) + "\n")
216 222 return "".join(r)
217 223
218 224 def capabilities(repo, proto):
219 caps = 'lookup changegroupsubset branchmap pushkey known getbundle'.split()
225 caps = ('lookup changegroupsubset branchmap pushkey known getbundle '
226 'unbundlehash').split()
220 227 if _allowstream(repo.ui):
221 228 requiredformats = repo.requirements & repo.supportedformats
222 229 # if our local revlogs are just revlogv1, add 'stream' cap
223 230 if not requiredformats - set(('revlogv1',)):
224 231 caps.append('stream')
225 232 # otherwise, add 'streamreqs' detailing our local revlog format
226 233 else:
227 234 caps.append('streamreqs=%s' % ','.join(requiredformats))
228 235 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
229 236 return ' '.join(caps)
230 237
231 238 def changegroup(repo, proto, roots):
232 239 nodes = decodelist(roots)
233 240 cg = repo.changegroup(nodes, 'serve')
234 241 return streamres(proto.groupchunks(cg))
235 242
236 243 def changegroupsubset(repo, proto, bases, heads):
237 244 bases = decodelist(bases)
238 245 heads = decodelist(heads)
239 246 cg = repo.changegroupsubset(bases, heads, 'serve')
240 247 return streamres(proto.groupchunks(cg))
241 248
242 249 def debugwireargs(repo, proto, one, two, others):
243 250 # only accept optional args from the known set
244 251 opts = options('debugwireargs', ['three', 'four'], others)
245 252 return repo.debugwireargs(one, two, **opts)
246 253
247 254 def getbundle(repo, proto, others):
248 255 opts = options('getbundle', ['heads', 'common'], others)
249 256 for k, v in opts.iteritems():
250 257 opts[k] = decodelist(v)
251 258 cg = repo.getbundle('serve', **opts)
252 259 return streamres(proto.groupchunks(cg))
253 260
254 261 def heads(repo, proto):
255 262 h = repo.heads()
256 263 return encodelist(h) + "\n"
257 264
258 265 def hello(repo, proto):
259 266 '''the hello command returns a set of lines describing various
260 267 interesting things about the server, in an RFC822-like format.
261 268 Currently the only one defined is "capabilities", which
262 269 consists of a line in the form:
263 270
264 271 capabilities: space separated list of tokens
265 272 '''
266 273 return "capabilities: %s\n" % (capabilities(repo, proto))
267 274
268 275 def listkeys(repo, proto, namespace):
269 276 d = pushkeymod.list(repo, encoding.tolocal(namespace)).items()
270 277 t = '\n'.join(['%s\t%s' % (encoding.fromlocal(k), encoding.fromlocal(v))
271 278 for k, v in d])
272 279 return t
273 280
274 281 def lookup(repo, proto, key):
275 282 try:
276 283 r = hex(repo.lookup(encoding.tolocal(key)))
277 284 success = 1
278 285 except Exception, inst:
279 286 r = str(inst)
280 287 success = 0
281 288 return "%s %s\n" % (success, r)
282 289
283 290 def known(repo, proto, nodes):
284 291 return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
285 292
286 293 def pushkey(repo, proto, namespace, key, old, new):
287 294 # compatibility with pre-1.8 clients which were accidentally
288 295 # sending raw binary nodes rather than utf-8-encoded hex
289 296 if len(new) == 20 and new.encode('string-escape') != new:
290 297 # looks like it could be a binary node
291 298 try:
292 299 u = new.decode('utf-8')
293 300 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
294 301 except UnicodeDecodeError:
295 302 pass # binary, leave unmodified
296 303 else:
297 304 new = encoding.tolocal(new) # normal path
298 305
299 306 r = pushkeymod.push(repo,
300 307 encoding.tolocal(namespace), encoding.tolocal(key),
301 308 encoding.tolocal(old), new)
302 309 return '%s\n' % int(r)
303 310
304 311 def _allowstream(ui):
305 312 return ui.configbool('server', 'uncompressed', True, untrusted=True)
306 313
307 314 def stream(repo, proto):
308 315 '''If the server supports streaming clone, it advertises the "stream"
309 316 capability with a value representing the version and flags of the repo
310 317 it is serving. Client checks to see if it understands the format.
311 318
312 319 The format is simple: the server writes out a line with the amount
313 320 of files, then the total amount of bytes to be transfered (separated
314 321 by a space). Then, for each file, the server first writes the filename
315 322 and filesize (separated by the null character), then the file contents.
316 323 '''
317 324
318 325 if not _allowstream(repo.ui):
319 326 return '1\n'
320 327
321 328 entries = []
322 329 total_bytes = 0
323 330 try:
324 331 # get consistent snapshot of repo, lock during scan
325 332 lock = repo.lock()
326 333 try:
327 334 repo.ui.debug('scanning\n')
328 335 for name, ename, size in repo.store.walk():
329 336 entries.append((name, size))
330 337 total_bytes += size
331 338 finally:
332 339 lock.release()
333 340 except error.LockError:
334 341 return '2\n' # error: 2
335 342
336 343 def streamer(repo, entries, total):
337 344 '''stream out all metadata files in repository.'''
338 345 yield '0\n' # success
339 346 repo.ui.debug('%d files, %d bytes to transfer\n' %
340 347 (len(entries), total_bytes))
341 348 yield '%d %d\n' % (len(entries), total_bytes)
342 349 for name, size in entries:
343 350 repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
344 351 # partially encode name over the wire for backwards compat
345 352 yield '%s\0%d\n' % (store.encodedir(name), size)
346 353 for chunk in util.filechunkiter(repo.sopener(name), limit=size):
347 354 yield chunk
348 355
349 356 return streamres(streamer(repo, entries, total_bytes))
350 357
351 358 def unbundle(repo, proto, heads):
352 359 their_heads = decodelist(heads)
353 360
354 361 def check_heads():
355 362 heads = repo.heads()
356 return their_heads == ['force'] or their_heads == heads
363 heads_hash = util.sha1(''.join(sorted(heads))).digest()
364 return (their_heads == ['force'] or their_heads == heads or
365 their_heads == ['hashed', heads_hash])
357 366
358 367 proto.redirect()
359 368
360 369 # fail early if possible
361 370 if not check_heads():
362 371 return pusherr('unsynced changes')
363 372
364 373 # write bundle data to temporary file because it can be big
365 374 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
366 375 fp = os.fdopen(fd, 'wb+')
367 376 r = 0
368 377 try:
369 378 proto.getfile(fp)
370 379 lock = repo.lock()
371 380 try:
372 381 if not check_heads():
373 382 # someone else committed/pushed/unbundled while we
374 383 # were transferring data
375 384 return pusherr('unsynced changes')
376 385
377 386 # push can proceed
378 387 fp.seek(0)
379 388 gen = changegroupmod.readbundle(fp, None)
380 389
381 390 try:
382 391 r = repo.addchangegroup(gen, 'serve', proto._client(),
383 392 lock=lock)
384 393 except util.Abort, inst:
385 394 sys.stderr.write("abort: %s\n" % inst)
386 395 finally:
387 396 lock.release()
388 397 return pushres(r)
389 398
390 399 finally:
391 400 fp.close()
392 401 os.unlink(tempname)
393 402
394 403 commands = {
395 404 'between': (between, 'pairs'),
396 405 'branchmap': (branchmap, ''),
397 406 'branches': (branches, 'nodes'),
398 407 'capabilities': (capabilities, ''),
399 408 'changegroup': (changegroup, 'roots'),
400 409 'changegroupsubset': (changegroupsubset, 'bases heads'),
401 410 'debugwireargs': (debugwireargs, 'one two *'),
402 411 'getbundle': (getbundle, '*'),
403 412 'heads': (heads, ''),
404 413 'hello': (hello, ''),
405 414 'known': (known, 'nodes'),
406 415 'listkeys': (listkeys, 'namespace'),
407 416 'lookup': (lookup, 'key'),
408 417 'pushkey': (pushkey, 'namespace key old new'),
409 418 'stream_out': (stream, ''),
410 419 'unbundle': (unbundle, 'heads'),
411 420 }
@@ -1,1119 +1,1119 b''
1 1 An attempt at more fully testing the hgweb web interface.
2 2 The following things are tested elsewhere and are therefore omitted:
3 3 - archive, tested in test-archive
4 4 - unbundle, tested in test-push-http
5 5 - changegroupsubset, tested in test-pull
6 6
7 7 Set up the repo
8 8
9 9 $ hg init test
10 10 $ cd test
11 11 $ mkdir da
12 12 $ echo foo > da/foo
13 13 $ echo foo > foo
14 14 $ hg ci -Ambase
15 15 adding da/foo
16 16 adding foo
17 17 $ hg tag 1.0
18 18 $ hg bookmark something
19 19 $ hg bookmark -r0 anotherthing
20 20 $ echo another > foo
21 21 $ hg branch stable
22 22 marked working directory as branch stable
23 23 $ hg ci -Ambranch
24 24 $ hg serve --config server.uncompressed=False -n test -p $HGPORT -d --pid-file=hg.pid -E errors.log
25 25 $ cat hg.pid >> $DAEMON_PIDS
26 26
27 27 Logs and changes
28 28
29 29 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/?style=atom'
30 30 200 Script output follows
31 31
32 32 <?xml version="1.0" encoding="ascii"?>
33 33 <feed xmlns="http://www.w3.org/2005/Atom">
34 34 <!-- Changelog -->
35 35 <id>http://*:$HGPORT/</id> (glob)
36 36 <link rel="self" href="http://*:$HGPORT/atom-log"/> (glob)
37 37 <link rel="alternate" href="http://*:$HGPORT/"/> (glob)
38 38 <title>test Changelog</title>
39 39 <updated>1970-01-01T00:00:00+00:00</updated>
40 40
41 41 <entry>
42 42 <title>branch</title>
43 43 <id>http://*:$HGPORT/#changeset-1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe</id> (glob)
44 44 <link href="http://*:$HGPORT/rev/1d22e65f027e"/> (glob)
45 45 <author>
46 46 <name>test</name>
47 47 <email>&#116;&#101;&#115;&#116;</email>
48 48 </author>
49 49 <updated>1970-01-01T00:00:00+00:00</updated>
50 50 <published>1970-01-01T00:00:00+00:00</published>
51 51 <content type="xhtml">
52 52 <div xmlns="http://www.w3.org/1999/xhtml">
53 53 <pre xml:space="preserve">branch</pre>
54 54 </div>
55 55 </content>
56 56 </entry>
57 57 <entry>
58 58 <title>Added tag 1.0 for changeset 2ef0ac749a14</title>
59 59 <id>http://*:$HGPORT/#changeset-a4f92ed23982be056b9852de5dfe873eaac7f0de</id> (glob)
60 60 <link href="http://*:$HGPORT/rev/a4f92ed23982"/> (glob)
61 61 <author>
62 62 <name>test</name>
63 63 <email>&#116;&#101;&#115;&#116;</email>
64 64 </author>
65 65 <updated>1970-01-01T00:00:00+00:00</updated>
66 66 <published>1970-01-01T00:00:00+00:00</published>
67 67 <content type="xhtml">
68 68 <div xmlns="http://www.w3.org/1999/xhtml">
69 69 <pre xml:space="preserve">Added tag 1.0 for changeset 2ef0ac749a14</pre>
70 70 </div>
71 71 </content>
72 72 </entry>
73 73 <entry>
74 74 <title>base</title>
75 75 <id>http://*:$HGPORT/#changeset-2ef0ac749a14e4f57a5a822464a0902c6f7f448f</id> (glob)
76 76 <link href="http://*:$HGPORT/rev/2ef0ac749a14"/> (glob)
77 77 <author>
78 78 <name>test</name>
79 79 <email>&#116;&#101;&#115;&#116;</email>
80 80 </author>
81 81 <updated>1970-01-01T00:00:00+00:00</updated>
82 82 <published>1970-01-01T00:00:00+00:00</published>
83 83 <content type="xhtml">
84 84 <div xmlns="http://www.w3.org/1999/xhtml">
85 85 <pre xml:space="preserve">base</pre>
86 86 </div>
87 87 </content>
88 88 </entry>
89 89
90 90 </feed>
91 91 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/1/?style=atom'
92 92 200 Script output follows
93 93
94 94 <?xml version="1.0" encoding="ascii"?>
95 95 <feed xmlns="http://www.w3.org/2005/Atom">
96 96 <!-- Changelog -->
97 97 <id>http://*:$HGPORT/</id> (glob)
98 98 <link rel="self" href="http://*:$HGPORT/atom-log"/> (glob)
99 99 <link rel="alternate" href="http://*:$HGPORT/"/> (glob)
100 100 <title>test Changelog</title>
101 101 <updated>1970-01-01T00:00:00+00:00</updated>
102 102
103 103 <entry>
104 104 <title>branch</title>
105 105 <id>http://*:$HGPORT/#changeset-1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe</id> (glob)
106 106 <link href="http://*:$HGPORT/rev/1d22e65f027e"/> (glob)
107 107 <author>
108 108 <name>test</name>
109 109 <email>&#116;&#101;&#115;&#116;</email>
110 110 </author>
111 111 <updated>1970-01-01T00:00:00+00:00</updated>
112 112 <published>1970-01-01T00:00:00+00:00</published>
113 113 <content type="xhtml">
114 114 <div xmlns="http://www.w3.org/1999/xhtml">
115 115 <pre xml:space="preserve">branch</pre>
116 116 </div>
117 117 </content>
118 118 </entry>
119 119 <entry>
120 120 <title>Added tag 1.0 for changeset 2ef0ac749a14</title>
121 121 <id>http://*:$HGPORT/#changeset-a4f92ed23982be056b9852de5dfe873eaac7f0de</id> (glob)
122 122 <link href="http://*:$HGPORT/rev/a4f92ed23982"/> (glob)
123 123 <author>
124 124 <name>test</name>
125 125 <email>&#116;&#101;&#115;&#116;</email>
126 126 </author>
127 127 <updated>1970-01-01T00:00:00+00:00</updated>
128 128 <published>1970-01-01T00:00:00+00:00</published>
129 129 <content type="xhtml">
130 130 <div xmlns="http://www.w3.org/1999/xhtml">
131 131 <pre xml:space="preserve">Added tag 1.0 for changeset 2ef0ac749a14</pre>
132 132 </div>
133 133 </content>
134 134 </entry>
135 135 <entry>
136 136 <title>base</title>
137 137 <id>http://*:$HGPORT/#changeset-2ef0ac749a14e4f57a5a822464a0902c6f7f448f</id> (glob)
138 138 <link href="http://*:$HGPORT/rev/2ef0ac749a14"/> (glob)
139 139 <author>
140 140 <name>test</name>
141 141 <email>&#116;&#101;&#115;&#116;</email>
142 142 </author>
143 143 <updated>1970-01-01T00:00:00+00:00</updated>
144 144 <published>1970-01-01T00:00:00+00:00</published>
145 145 <content type="xhtml">
146 146 <div xmlns="http://www.w3.org/1999/xhtml">
147 147 <pre xml:space="preserve">base</pre>
148 148 </div>
149 149 </content>
150 150 </entry>
151 151
152 152 </feed>
153 153 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log/1/foo/?style=atom'
154 154 200 Script output follows
155 155
156 156 <?xml version="1.0" encoding="ascii"?>
157 157 <feed xmlns="http://www.w3.org/2005/Atom">
158 158 <id>http://*:$HGPORT/atom-log/tip/foo</id> (glob)
159 159 <link rel="self" href="http://*:$HGPORT/atom-log/tip/foo"/> (glob)
160 160 <title>test: foo history</title>
161 161 <updated>1970-01-01T00:00:00+00:00</updated>
162 162
163 163 <entry>
164 164 <title>base</title>
165 165 <id>http://*:$HGPORT/#changeset-2ef0ac749a14e4f57a5a822464a0902c6f7f448f</id> (glob)
166 166 <link href="http://*:$HGPORT/rev/2ef0ac749a14"/> (glob)
167 167 <author>
168 168 <name>test</name>
169 169 <email>&#116;&#101;&#115;&#116;</email>
170 170 </author>
171 171 <updated>1970-01-01T00:00:00+00:00</updated>
172 172 <published>1970-01-01T00:00:00+00:00</published>
173 173 <content type="xhtml">
174 174 <div xmlns="http://www.w3.org/1999/xhtml">
175 175 <pre xml:space="preserve">base</pre>
176 176 </div>
177 177 </content>
178 178 </entry>
179 179
180 180 </feed>
181 181 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/shortlog/'
182 182 200 Script output follows
183 183
184 184 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
185 185 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
186 186 <head>
187 187 <link rel="icon" href="/static/hgicon.png" type="image/png" />
188 188 <meta name="robots" content="index, nofollow" />
189 189 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
190 190
191 191 <title>test: log</title>
192 192 <link rel="alternate" type="application/atom+xml"
193 193 href="/atom-log" title="Atom feed for test" />
194 194 <link rel="alternate" type="application/rss+xml"
195 195 href="/rss-log" title="RSS feed for test" />
196 196 </head>
197 197 <body>
198 198
199 199 <div class="container">
200 200 <div class="menu">
201 201 <div class="logo">
202 202 <a href="http://mercurial.selenic.com/">
203 203 <img src="/static/hglogo.png" alt="mercurial" /></a>
204 204 </div>
205 205 <ul>
206 206 <li class="active">log</li>
207 207 <li><a href="/graph/1d22e65f027e">graph</a></li>
208 208 <li><a href="/tags">tags</a></li>
209 209 <li><a href="/bookmarks">bookmarks</a></li>
210 210 <li><a href="/branches">branches</a></li>
211 211 </ul>
212 212 <ul>
213 213 <li><a href="/rev/1d22e65f027e">changeset</a></li>
214 214 <li><a href="/file/1d22e65f027e">browse</a></li>
215 215 </ul>
216 216 <ul>
217 217
218 218 </ul>
219 219 <ul>
220 220 <li><a href="/help">help</a></li>
221 221 </ul>
222 222 </div>
223 223
224 224 <div class="main">
225 225 <h2><a href="/">test</a></h2>
226 226 <h3>log</h3>
227 227
228 228 <form class="search" action="/log">
229 229
230 230 <p><input name="rev" id="search1" type="text" size="30" /></p>
231 231 <div id="hint">find changesets by author, revision,
232 232 files, or words in the commit message</div>
233 233 </form>
234 234
235 235 <div class="navigate">
236 236 <a href="/shortlog/2?revcount=30">less</a>
237 237 <a href="/shortlog/2?revcount=120">more</a>
238 238 | rev 2: <a href="/shortlog/2ef0ac749a14">(0)</a> <a href="/shortlog/tip">tip</a>
239 239 </div>
240 240
241 241 <table class="bigtable">
242 242 <tr>
243 243 <th class="age">age</th>
244 244 <th class="author">author</th>
245 245 <th class="description">description</th>
246 246 </tr>
247 247 <tr class="parity0">
248 248 <td class="age">1970-01-01</td>
249 249 <td class="author">test</td>
250 250 <td class="description"><a href="/rev/1d22e65f027e">branch</a><span class="branchhead">stable</span> <span class="tag">tip</span> <span class="tag">something</span> </td>
251 251 </tr>
252 252 <tr class="parity1">
253 253 <td class="age">1970-01-01</td>
254 254 <td class="author">test</td>
255 255 <td class="description"><a href="/rev/a4f92ed23982">Added tag 1.0 for changeset 2ef0ac749a14</a><span class="branchhead">default</span> </td>
256 256 </tr>
257 257 <tr class="parity0">
258 258 <td class="age">1970-01-01</td>
259 259 <td class="author">test</td>
260 260 <td class="description"><a href="/rev/2ef0ac749a14">base</a><span class="tag">1.0</span> <span class="tag">anotherthing</span> </td>
261 261 </tr>
262 262
263 263 </table>
264 264
265 265 <div class="navigate">
266 266 <a href="/shortlog/2?revcount=30">less</a>
267 267 <a href="/shortlog/2?revcount=120">more</a>
268 268 | rev 2: <a href="/shortlog/2ef0ac749a14">(0)</a> <a href="/shortlog/tip">tip</a>
269 269 </div>
270 270
271 271 </div>
272 272 </div>
273 273
274 274
275 275
276 276 </body>
277 277 </html>
278 278
279 279 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/rev/0/'
280 280 200 Script output follows
281 281
282 282 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
283 283 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
284 284 <head>
285 285 <link rel="icon" href="/static/hgicon.png" type="image/png" />
286 286 <meta name="robots" content="index, nofollow" />
287 287 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
288 288
289 289 <title>test: 2ef0ac749a14</title>
290 290 </head>
291 291 <body>
292 292 <div class="container">
293 293 <div class="menu">
294 294 <div class="logo">
295 295 <a href="http://mercurial.selenic.com/">
296 296 <img src="/static/hglogo.png" alt="mercurial" /></a>
297 297 </div>
298 298 <ul>
299 299 <li><a href="/shortlog/2ef0ac749a14">log</a></li>
300 300 <li><a href="/graph/2ef0ac749a14">graph</a></li>
301 301 <li><a href="/tags">tags</a></li>
302 302 <li><a href="/bookmarks">bookmarks</a></li>
303 303 <li><a href="/branches">branches</a></li>
304 304 </ul>
305 305 <ul>
306 306 <li class="active">changeset</li>
307 307 <li><a href="/raw-rev/2ef0ac749a14">raw</a></li>
308 308 <li><a href="/file/2ef0ac749a14">browse</a></li>
309 309 </ul>
310 310 <ul>
311 311
312 312 </ul>
313 313 <ul>
314 314 <li><a href="/help">help</a></li>
315 315 </ul>
316 316 </div>
317 317
318 318 <div class="main">
319 319
320 320 <h2><a href="/">test</a></h2>
321 321 <h3>changeset 0:2ef0ac749a14 <span class="tag">1.0</span> <span class="tag">anotherthing</span> </h3>
322 322
323 323 <form class="search" action="/log">
324 324
325 325 <p><input name="rev" id="search1" type="text" size="30" /></p>
326 326 <div id="hint">find changesets by author, revision,
327 327 files, or words in the commit message</div>
328 328 </form>
329 329
330 330 <div class="description">base</div>
331 331
332 332 <table id="changesetEntry">
333 333 <tr>
334 334 <th class="author">author</th>
335 335 <td class="author">&#116;&#101;&#115;&#116;</td>
336 336 </tr>
337 337 <tr>
338 338 <th class="date">date</th>
339 339 <td class="date">Thu Jan 01 00:00:00 1970 +0000 (1970-01-01)</td></tr>
340 340 <tr>
341 341 <th class="author">parents</th>
342 342 <td class="author"></td>
343 343 </tr>
344 344 <tr>
345 345 <th class="author">children</th>
346 346 <td class="author"> <a href="/rev/a4f92ed23982">a4f92ed23982</a></td>
347 347 </tr>
348 348 <tr>
349 349 <th class="files">files</th>
350 350 <td class="files"><a href="/file/2ef0ac749a14/da/foo">da/foo</a> <a href="/file/2ef0ac749a14/foo">foo</a> </td>
351 351 </tr>
352 352 </table>
353 353
354 354 <div class="overflow">
355 355 <div class="sourcefirst"> line diff</div>
356 356
357 357 <div class="source bottomline parity0"><pre><a href="#l1.1" id="l1.1"> 1.1</a> <span class="minusline">--- /dev/null Thu Jan 01 00:00:00 1970 +0000
358 358 </span><a href="#l1.2" id="l1.2"> 1.2</a> <span class="plusline">+++ b/da/foo Thu Jan 01 00:00:00 1970 +0000
359 359 </span><a href="#l1.3" id="l1.3"> 1.3</a> <span class="atline">@@ -0,0 +1,1 @@
360 360 </span><a href="#l1.4" id="l1.4"> 1.4</a> <span class="plusline">+foo
361 361 </span></pre></div><div class="source bottomline parity1"><pre><a href="#l2.1" id="l2.1"> 2.1</a> <span class="minusline">--- /dev/null Thu Jan 01 00:00:00 1970 +0000
362 362 </span><a href="#l2.2" id="l2.2"> 2.2</a> <span class="plusline">+++ b/foo Thu Jan 01 00:00:00 1970 +0000
363 363 </span><a href="#l2.3" id="l2.3"> 2.3</a> <span class="atline">@@ -0,0 +1,1 @@
364 364 </span><a href="#l2.4" id="l2.4"> 2.4</a> <span class="plusline">+foo
365 365 </span></pre></div>
366 366 </div>
367 367
368 368 </div>
369 369 </div>
370 370
371 371
372 372 </body>
373 373 </html>
374 374
375 375 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/rev/1/?style=raw'
376 376 200 Script output follows
377 377
378 378
379 379 # HG changeset patch
380 380 # User test
381 381 # Date 0 0
382 382 # Node ID a4f92ed23982be056b9852de5dfe873eaac7f0de
383 383 # Parent 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
384 384 Added tag 1.0 for changeset 2ef0ac749a14
385 385
386 386 diff -r 2ef0ac749a14 -r a4f92ed23982 .hgtags
387 387 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
388 388 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
389 389 @@ -0,0 +1,1 @@
390 390 +2ef0ac749a14e4f57a5a822464a0902c6f7f448f 1.0
391 391
392 392 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/log?rev=base'
393 393 200 Script output follows
394 394
395 395 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
396 396 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
397 397 <head>
398 398 <link rel="icon" href="/static/hgicon.png" type="image/png" />
399 399 <meta name="robots" content="index, nofollow" />
400 400 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
401 401
402 402 <title>test: searching for base</title>
403 403 </head>
404 404 <body>
405 405
406 406 <div class="container">
407 407 <div class="menu">
408 408 <div class="logo">
409 409 <a href="http://mercurial.selenic.com/">
410 410 <img src="/static/hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
411 411 </div>
412 412 <ul>
413 413 <li><a href="/shortlog">log</a></li>
414 414 <li><a href="/graph">graph</a></li>
415 415 <li><a href="/tags">tags</a></li>
416 416 <li><a href="/bookmarks">bookmarks</a></li>
417 417 <li><a href="/branches">branches</a></li>
418 418 <li><a href="/help">help</a></li>
419 419 </ul>
420 420 </div>
421 421
422 422 <div class="main">
423 423 <h2><a href="/">test</a></h2>
424 424 <h3>searching for 'base'</h3>
425 425
426 426 <form class="search" action="/log">
427 427
428 428 <p><input name="rev" id="search1" type="text" size="30"></p>
429 429 <div id="hint">find changesets by author, revision,
430 430 files, or words in the commit message</div>
431 431 </form>
432 432
433 433 <div class="navigate">
434 434 <a href="/search/?rev=base&revcount=5">less</a>
435 435 <a href="/search/?rev=base&revcount=20">more</a>
436 436 </div>
437 437
438 438 <table class="bigtable">
439 439 <tr>
440 440 <th class="age">age</th>
441 441 <th class="author">author</th>
442 442 <th class="description">description</th>
443 443 </tr>
444 444 <tr class="parity0">
445 445 <td class="age">1970-01-01</td>
446 446 <td class="author">test</td>
447 447 <td class="description"><a href="/rev/2ef0ac749a14">base</a><span class="tag">1.0</span> <span class="tag">anotherthing</span> </td>
448 448 </tr>
449 449
450 450 </table>
451 451
452 452 <div class="navigate">
453 453 <a href="/search/?rev=base&revcount=5">less</a>
454 454 <a href="/search/?rev=base&revcount=20">more</a>
455 455 </div>
456 456
457 457 </div>
458 458 </div>
459 459
460 460
461 461
462 462 </body>
463 463 </html>
464 464
465 465
466 466 File-related
467 467
468 468 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/foo/?style=raw'
469 469 200 Script output follows
470 470
471 471 foo
472 472 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/annotate/1/foo/?style=raw'
473 473 200 Script output follows
474 474
475 475
476 476 test@0: foo
477 477
478 478
479 479
480 480
481 481 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/?style=raw'
482 482 200 Script output follows
483 483
484 484
485 485 drwxr-xr-x da
486 486 -rw-r--r-- 45 .hgtags
487 487 -rw-r--r-- 4 foo
488 488
489 489
490 490 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/file/1/foo'
491 491 200 Script output follows
492 492
493 493 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
494 494 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
495 495 <head>
496 496 <link rel="icon" href="/static/hgicon.png" type="image/png" />
497 497 <meta name="robots" content="index, nofollow" />
498 498 <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
499 499
500 500 <title>test: a4f92ed23982 foo</title>
501 501 </head>
502 502 <body>
503 503
504 504 <div class="container">
505 505 <div class="menu">
506 506 <div class="logo">
507 507 <a href="http://mercurial.selenic.com/">
508 508 <img src="/static/hglogo.png" alt="mercurial" /></a>
509 509 </div>
510 510 <ul>
511 511 <li><a href="/shortlog/a4f92ed23982">log</a></li>
512 512 <li><a href="/graph/a4f92ed23982">graph</a></li>
513 513 <li><a href="/tags">tags</a></li>
514 514 <li><a href="/branches">branches</a></li>
515 515 </ul>
516 516 <ul>
517 517 <li><a href="/rev/a4f92ed23982">changeset</a></li>
518 518 <li><a href="/file/a4f92ed23982/">browse</a></li>
519 519 </ul>
520 520 <ul>
521 521 <li class="active">file</li>
522 522 <li><a href="/file/tip/foo">latest</a></li>
523 523 <li><a href="/diff/a4f92ed23982/foo">diff</a></li>
524 524 <li><a href="/annotate/a4f92ed23982/foo">annotate</a></li>
525 525 <li><a href="/log/a4f92ed23982/foo">file log</a></li>
526 526 <li><a href="/raw-file/a4f92ed23982/foo">raw</a></li>
527 527 </ul>
528 528 <ul>
529 529 <li><a href="/help">help</a></li>
530 530 </ul>
531 531 </div>
532 532
533 533 <div class="main">
534 534 <h2><a href="/">test</a></h2>
535 535 <h3>view foo @ 1:a4f92ed23982</h3>
536 536
537 537 <form class="search" action="/log">
538 538
539 539 <p><input name="rev" id="search1" type="text" size="30" /></p>
540 540 <div id="hint">find changesets by author, revision,
541 541 files, or words in the commit message</div>
542 542 </form>
543 543
544 544 <div class="description">Added tag 1.0 for changeset 2ef0ac749a14</div>
545 545
546 546 <table id="changesetEntry">
547 547 <tr>
548 548 <th class="author">author</th>
549 549 <td class="author">&#116;&#101;&#115;&#116;</td>
550 550 </tr>
551 551 <tr>
552 552 <th class="date">date</th>
553 553 <td class="date">Thu Jan 01 00:00:00 1970 +0000 (1970-01-01)</td>
554 554 </tr>
555 555 <tr>
556 556 <th class="author">parents</th>
557 557 <td class="author"></td>
558 558 </tr>
559 559 <tr>
560 560 <th class="author">children</th>
561 561 <td class="author"><a href="/file/1d22e65f027e/foo">1d22e65f027e</a> </td>
562 562 </tr>
563 563
564 564 </table>
565 565
566 566 <div class="overflow">
567 567 <div class="sourcefirst"> line source</div>
568 568
569 569 <div class="parity0 source"><a href="#l1" id="l1"> 1</a> foo
570 570 </div>
571 571 <div class="sourcelast"></div>
572 572 </div>
573 573 </div>
574 574 </div>
575 575
576 576
577 577
578 578 </body>
579 579 </html>
580 580
581 581 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/filediff/1/foo/?style=raw'
582 582 200 Script output follows
583 583
584 584
585 585 diff -r 000000000000 -r a4f92ed23982 foo
586 586 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
587 587 +++ b/foo Thu Jan 01 00:00:00 1970 +0000
588 588 @@ -0,0 +1,1 @@
589 589 +foo
590 590
591 591
592 592
593 593
594 594
595 595 Overviews
596 596
597 597 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/raw-tags'
598 598 200 Script output follows
599 599
600 600 tip 1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe
601 601 1.0 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
602 602 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/raw-branches'
603 603 200 Script output follows
604 604
605 605 stable 1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe open
606 606 default a4f92ed23982be056b9852de5dfe873eaac7f0de inactive
607 607 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/raw-bookmarks'
608 608 200 Script output follows
609 609
610 610 anotherthing 2ef0ac749a14e4f57a5a822464a0902c6f7f448f
611 611 something 1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe
612 612 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/summary/?style=gitweb'
613 613 200 Script output follows
614 614
615 615 <?xml version="1.0" encoding="ascii"?>
616 616 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
617 617 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
618 618 <head>
619 619 <link rel="icon" href="/static/hgicon.png" type="image/png" />
620 620 <meta name="robots" content="index, nofollow"/>
621 621 <link rel="stylesheet" href="/static/style-gitweb.css" type="text/css" />
622 622
623 623
624 624 <title>test: Summary</title>
625 625 <link rel="alternate" type="application/atom+xml"
626 626 href="/atom-log" title="Atom feed for test"/>
627 627 <link rel="alternate" type="application/rss+xml"
628 628 href="/rss-log" title="RSS feed for test"/>
629 629 </head>
630 630 <body>
631 631
632 632 <div class="page_header">
633 633 <a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="/summary?style=gitweb">test</a> / summary
634 634
635 635 <form action="/log">
636 636 <input type="hidden" name="style" value="gitweb" />
637 637 <div class="search">
638 638 <input type="text" name="rev" />
639 639 </div>
640 640 </form>
641 641 </div>
642 642
643 643 <div class="page_nav">
644 644 summary |
645 645 <a href="/shortlog?style=gitweb">shortlog</a> |
646 646 <a href="/log?style=gitweb">changelog</a> |
647 647 <a href="/graph?style=gitweb">graph</a> |
648 648 <a href="/tags?style=gitweb">tags</a> |
649 649 <a href="/bookmarks?style=gitweb">bookmarks</a> |
650 650 <a href="/branches?style=gitweb">branches</a> |
651 651 <a href="/file/1d22e65f027e?style=gitweb">files</a> |
652 652 <a href="/help?style=gitweb">help</a>
653 653 <br/>
654 654 </div>
655 655
656 656 <div class="title">&nbsp;</div>
657 657 <table cellspacing="0">
658 658 <tr><td>description</td><td>unknown</td></tr>
659 659 <tr><td>owner</td><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></tr>
660 660 <tr><td>last change</td><td>Thu, 01 Jan 1970 00:00:00 +0000</td></tr>
661 661 </table>
662 662
663 663 <div><a class="title" href="/shortlog?style=gitweb">changes</a></div>
664 664 <table cellspacing="0">
665 665
666 666 <tr class="parity0">
667 667 <td class="age"><i>1970-01-01</i></td>
668 668 <td><i>test</i></td>
669 669 <td>
670 670 <a class="list" href="/rev/1d22e65f027e?style=gitweb">
671 671 <b>branch</b>
672 672 <span class="logtags"><span class="branchtag" title="stable">stable</span> <span class="tagtag" title="tip">tip</span> <span class="bookmarktag" title="something">something</span> </span>
673 673 </a>
674 674 </td>
675 675 <td class="link" nowrap>
676 676 <a href="/rev/1d22e65f027e?style=gitweb">changeset</a> |
677 677 <a href="/file/1d22e65f027e?style=gitweb">files</a>
678 678 </td>
679 679 </tr>
680 680 <tr class="parity1">
681 681 <td class="age"><i>1970-01-01</i></td>
682 682 <td><i>test</i></td>
683 683 <td>
684 684 <a class="list" href="/rev/a4f92ed23982?style=gitweb">
685 685 <b>Added tag 1.0 for changeset 2ef0ac749a14</b>
686 686 <span class="logtags"><span class="branchtag" title="default">default</span> </span>
687 687 </a>
688 688 </td>
689 689 <td class="link" nowrap>
690 690 <a href="/rev/a4f92ed23982?style=gitweb">changeset</a> |
691 691 <a href="/file/a4f92ed23982?style=gitweb">files</a>
692 692 </td>
693 693 </tr>
694 694 <tr class="parity0">
695 695 <td class="age"><i>1970-01-01</i></td>
696 696 <td><i>test</i></td>
697 697 <td>
698 698 <a class="list" href="/rev/2ef0ac749a14?style=gitweb">
699 699 <b>base</b>
700 700 <span class="logtags"><span class="tagtag" title="1.0">1.0</span> <span class="bookmarktag" title="anotherthing">anotherthing</span> </span>
701 701 </a>
702 702 </td>
703 703 <td class="link" nowrap>
704 704 <a href="/rev/2ef0ac749a14?style=gitweb">changeset</a> |
705 705 <a href="/file/2ef0ac749a14?style=gitweb">files</a>
706 706 </td>
707 707 </tr>
708 708 <tr class="light"><td colspan="4"><a class="list" href="/shortlog?style=gitweb">...</a></td></tr>
709 709 </table>
710 710
711 711 <div><a class="title" href="/tags?style=gitweb">tags</a></div>
712 712 <table cellspacing="0">
713 713
714 714 <tr class="parity0">
715 715 <td class="age"><i>1970-01-01</i></td>
716 716 <td><a class="list" href="/rev/2ef0ac749a14?style=gitweb"><b>1.0</b></a></td>
717 717 <td class="link">
718 718 <a href="/rev/2ef0ac749a14?style=gitweb">changeset</a> |
719 719 <a href="/log/2ef0ac749a14?style=gitweb">changelog</a> |
720 720 <a href="/file/2ef0ac749a14?style=gitweb">files</a>
721 721 </td>
722 722 </tr>
723 723 <tr class="light"><td colspan="3"><a class="list" href="/tags?style=gitweb">...</a></td></tr>
724 724 </table>
725 725
726 726 <div><a class="title" href="/bookmarks?style=gitweb">bookmarks</a></div>
727 727 <table cellspacing="0">
728 728
729 729 <tr class="parity0">
730 730 <td class="age"><i>1970-01-01</i></td>
731 731 <td><a class="list" href="/rev/2ef0ac749a14?style=gitweb"><b>anotherthing</b></a></td>
732 732 <td class="link">
733 733 <a href="/rev/2ef0ac749a14?style=gitweb">changeset</a> |
734 734 <a href="/log/2ef0ac749a14?style=gitweb">changelog</a> |
735 735 <a href="/file/2ef0ac749a14?style=gitweb">files</a>
736 736 </td>
737 737 </tr>
738 738 <tr class="parity1">
739 739 <td class="age"><i>1970-01-01</i></td>
740 740 <td><a class="list" href="/rev/1d22e65f027e?style=gitweb"><b>something</b></a></td>
741 741 <td class="link">
742 742 <a href="/rev/1d22e65f027e?style=gitweb">changeset</a> |
743 743 <a href="/log/1d22e65f027e?style=gitweb">changelog</a> |
744 744 <a href="/file/1d22e65f027e?style=gitweb">files</a>
745 745 </td>
746 746 </tr>
747 747 <tr class="light"><td colspan="3"><a class="list" href="/bookmarks?style=gitweb">...</a></td></tr>
748 748 </table>
749 749
750 750 <div><a class="title" href="#">branches</a></div>
751 751 <table cellspacing="0">
752 752
753 753 <tr class="parity0">
754 754 <td class="age"><i>1970-01-01</i></td>
755 755 <td><a class="list" href="/shortlog/1d22e65f027e?style=gitweb"><b>1d22e65f027e</b></a></td>
756 756 <td class="">stable</td>
757 757 <td class="link">
758 758 <a href="/changeset/1d22e65f027e?style=gitweb">changeset</a> |
759 759 <a href="/log/1d22e65f027e?style=gitweb">changelog</a> |
760 760 <a href="/file/1d22e65f027e?style=gitweb">files</a>
761 761 </td>
762 762 </tr>
763 763 <tr class="parity1">
764 764 <td class="age"><i>1970-01-01</i></td>
765 765 <td><a class="list" href="/shortlog/a4f92ed23982?style=gitweb"><b>a4f92ed23982</b></a></td>
766 766 <td class="">default</td>
767 767 <td class="link">
768 768 <a href="/changeset/a4f92ed23982?style=gitweb">changeset</a> |
769 769 <a href="/log/a4f92ed23982?style=gitweb">changelog</a> |
770 770 <a href="/file/a4f92ed23982?style=gitweb">files</a>
771 771 </td>
772 772 </tr>
773 773 <tr class="light">
774 774 <td colspan="4"><a class="list" href="#">...</a></td>
775 775 </tr>
776 776 </table>
777 777 <div class="page_footer">
778 778 <div class="page_footer_text">test</div>
779 779 <div class="rss_logo">
780 780 <a href="/rss-log">RSS</a>
781 781 <a href="/atom-log">Atom</a>
782 782 </div>
783 783 <br />
784 784
785 785 </div>
786 786 </body>
787 787 </html>
788 788
789 789 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/graph/?style=gitweb'
790 790 200 Script output follows
791 791
792 792 <?xml version="1.0" encoding="ascii"?>
793 793 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
794 794 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
795 795 <head>
796 796 <link rel="icon" href="/static/hgicon.png" type="image/png" />
797 797 <meta name="robots" content="index, nofollow"/>
798 798 <link rel="stylesheet" href="/static/style-gitweb.css" type="text/css" />
799 799
800 800
801 801 <title>test: Graph</title>
802 802 <link rel="alternate" type="application/atom+xml"
803 803 href="/atom-log" title="Atom feed for test"/>
804 804 <link rel="alternate" type="application/rss+xml"
805 805 href="/rss-log" title="RSS feed for test"/>
806 806 <!--[if IE]><script type="text/javascript" src="/static/excanvas.js"></script><![endif]-->
807 807 </head>
808 808 <body>
809 809
810 810 <div class="page_header">
811 811 <a href="http://mercurial.selenic.com/" title="Mercurial" style="float: right;">Mercurial</a><a href="/summary?style=gitweb">test</a> / graph
812 812 </div>
813 813
814 814 <form action="/log">
815 815 <input type="hidden" name="style" value="gitweb" />
816 816 <div class="search">
817 817 <input type="text" name="rev" />
818 818 </div>
819 819 </form>
820 820 <div class="page_nav">
821 821 <a href="/summary?style=gitweb">summary</a> |
822 822 <a href="/shortlog?style=gitweb">shortlog</a> |
823 823 <a href="/log/2?style=gitweb">changelog</a> |
824 824 graph |
825 825 <a href="/tags?style=gitweb">tags</a> |
826 826 <a href="/bookmarks?style=gitweb">bookmarks</a> |
827 827 <a href="/branches?style=gitweb">branches</a> |
828 828 <a href="/file/1d22e65f027e?style=gitweb">files</a> |
829 829 <a href="/help?style=gitweb">help</a>
830 830 <br/>
831 831 <a href="/graph/2?style=gitweb&revcount=30">less</a>
832 832 <a href="/graph/2?style=gitweb&revcount=120">more</a>
833 833 | <a href="/graph/2ef0ac749a14?style=gitweb">(0)</a> <a href="/graph/2ef0ac749a14?style=gitweb">-2</a> <a href="/graph/tip?style=gitweb">tip</a> <br/>
834 834 </div>
835 835
836 836 <div class="title">&nbsp;</div>
837 837
838 838 <noscript>The revision graph only works with JavaScript-enabled browsers.</noscript>
839 839
840 840 <div id="wrapper">
841 841 <ul id="nodebgs"></ul>
842 842 <canvas id="graph" width="480" height="129"></canvas>
843 843 <ul id="graphnodes"></ul>
844 844 </div>
845 845
846 846 <script type="text/javascript" src="/static/graph.js"></script>
847 847 <script>
848 848 <!-- hide script content
849 849
850 850 var data = [["1d22e65f027e", [0, 1], [[0, 0, 1]], "branch", "test", "1970-01-01", ["stable", true], ["tip"], ["something"]], ["a4f92ed23982", [0, 1], [[0, 0, 1]], "Added tag 1.0 for changeset 2ef0ac749a14", "test", "1970-01-01", ["default", true], [], []], ["2ef0ac749a14", [0, 1], [], "base", "test", "1970-01-01", ["default", false], ["1.0"], ["anotherthing"]]];
851 851 var graph = new Graph();
852 852 graph.scale(39);
853 853
854 854 graph.edge = function(x0, y0, x1, y1, color) {
855 855
856 856 this.setColor(color, 0.0, 0.65);
857 857 this.ctx.beginPath();
858 858 this.ctx.moveTo(x0, y0);
859 859 this.ctx.lineTo(x1, y1);
860 860 this.ctx.stroke();
861 861
862 862 }
863 863
864 864 var revlink = '<li style="_STYLE"><span class="desc">';
865 865 revlink += '<a class="list" href="/rev/_NODEID?style=gitweb" title="_NODEID"><b>_DESC</b></a>';
866 866 revlink += '</span> _TAGS';
867 867 revlink += '<span class="info">_DATE, by _USER</span></li>';
868 868
869 869 graph.vertex = function(x, y, color, parity, cur) {
870 870
871 871 this.ctx.beginPath();
872 872 color = this.setColor(color, 0.25, 0.75);
873 873 this.ctx.arc(x, y, radius, 0, Math.PI * 2, true);
874 874 this.ctx.fill();
875 875
876 876 var bg = '<li class="bg parity' + parity + '"></li>';
877 877 var left = (this.columns + 1) * this.bg_height;
878 878 var nstyle = 'padding-left: ' + left + 'px;';
879 879 var item = revlink.replace(/_STYLE/, nstyle);
880 880 item = item.replace(/_PARITY/, 'parity' + parity);
881 881 item = item.replace(/_NODEID/, cur[0]);
882 882 item = item.replace(/_NODEID/, cur[0]);
883 883 item = item.replace(/_DESC/, cur[3]);
884 884 item = item.replace(/_USER/, cur[4]);
885 885 item = item.replace(/_DATE/, cur[5]);
886 886
887 887 var tagspan = '';
888 888 if (cur[7].length || cur[8].length || (cur[6][0] != 'default' || cur[6][1])) {
889 889 tagspan = '<span class="logtags">';
890 890 if (cur[6][1]) {
891 891 tagspan += '<span class="branchtag" title="' + cur[6][0] + '">';
892 892 tagspan += cur[6][0] + '</span> ';
893 893 } else if (!cur[6][1] && cur[6][0] != 'default') {
894 894 tagspan += '<span class="inbranchtag" title="' + cur[6][0] + '">';
895 895 tagspan += cur[6][0] + '</span> ';
896 896 }
897 897 if (cur[7].length) {
898 898 for (var t in cur[7]) {
899 899 var tag = cur[7][t];
900 900 tagspan += '<span class="tagtag">' + tag + '</span> ';
901 901 }
902 902 }
903 903 if (cur[8].length) {
904 904 for (var t in cur[8]) {
905 905 var bookmark = cur[8][t];
906 906 tagspan += '<span class="bookmarktag">' + bookmark + '</span> ';
907 907 }
908 908 }
909 909 tagspan += '</span>';
910 910 }
911 911
912 912 item = item.replace(/_TAGS/, tagspan);
913 913 return [bg, item];
914 914
915 915 }
916 916
917 917 graph.render(data);
918 918
919 919 // stop hiding script -->
920 920 </script>
921 921
922 922 <div class="page_nav">
923 923 <a href="/graph/2?style=gitweb&revcount=30">less</a>
924 924 <a href="/graph/2?style=gitweb&revcount=120">more</a>
925 925 | <a href="/graph/2ef0ac749a14?style=gitweb">(0)</a> <a href="/graph/2ef0ac749a14?style=gitweb">-2</a> <a href="/graph/tip?style=gitweb">tip</a>
926 926 </div>
927 927
928 928 <div class="page_footer">
929 929 <div class="page_footer_text">test</div>
930 930 <div class="rss_logo">
931 931 <a href="/rss-log">RSS</a>
932 932 <a href="/atom-log">Atom</a>
933 933 </div>
934 934 <br />
935 935
936 936 </div>
937 937 </body>
938 938 </html>
939 939
940 940
941 941 capabilities
942 942
943 943 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=capabilities'; echo
944 944 200 Script output follows
945 945
946 lookup changegroupsubset branchmap pushkey known getbundle unbundle=HG10GZ,HG10BZ,HG10UN
946 lookup changegroupsubset branchmap pushkey known getbundle unbundlehash unbundle=HG10GZ,HG10BZ,HG10UN
947 947
948 948 heads
949 949
950 950 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=heads'
951 951 200 Script output follows
952 952
953 953 1d22e65f027e5a0609357e7d8e7508cd2ba5d2fe
954 954
955 955 branches
956 956
957 957 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=branches&nodes=0000000000000000000000000000000000000000'
958 958 200 Script output follows
959 959
960 960 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000
961 961
962 962 changegroup
963 963
964 964 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=changegroup&roots=0000000000000000000000000000000000000000'
965 965 200 Script output follows
966 966
967 967 x\x9c\xbdTMHUA\x14\xbe\xa8\xf9\xec\xda&\x10\x11*\xb8\x88\x81\x99\xbef\xe6\xce\xbdw\xc6\xf2a\x16E\x1b\x11[%\x98\xcc\xaf\x8f\x8c\xf7\xc0\xf7\x82 (esc)
968 968 4\x11KP2m\x95\xad*\xabE\x05AP\xd0\xc22Z\x14\xf9\x03\xb9j\xa3\x9b$\xa4MJ\xb4\x90\xc0\x9a\x9bO0\x10\xdf\x13\xa2\x81\x0f\x869g\xe6|\xe7\x9c\xef\x8ceY\xf7\xa2KO\xd2\xb7K\x16~\\n\xe9\xad\x90w\x86\xab\x93W\x8e\xdf\xb0r\\Y\xee6(\xa2)\xf6\x95\xc6\x01\xe4\x1az\x80R\xe8kN\x98\xe7R\xa4\xa9K@\xe0!A\xb4k\xa7U*m\x03\x07\xd8\x92\x1d\xd2\xc9\xa4\x1d\xc2\xe6,\xa5\xcc+\x1f\xef\xafDgi\xef\xab\x1d\x1d\xb7\x9a\xe7[W\xfbc\x8f\xde-\xcd\xe7\xcaz\xb3\xbb\x19\xd3\x81\x10>c>\x08\x00"X\x11\xc2\x84@\xd2\xe7B*L\x00\x01P\x04R\xc3@\xbaB0\xdb8#\x83:\x83\xa2h\xbc=\xcd\xdaS\xe1Y,L\xd3\xa0\xf2\xa8\x94J:\xe6\xd8\x81Q\xe0\xe8d\xa7#\xe2,\xd1\xaeR*\xed \xa5\x01\x13\x01\xa6\x0cb\xe3;\xbe\xaf\xfcK[^wK\xe1N\xaf\xbbk\xe8B\xd1\xf4\xc1\x07\xb3\xab[\x10\xfdkmvwcB\xa6\xa4\xd4G\xc4D\xc2\x141\xad\x91\x10\x00\x08J\x81\xcb}\xee \xee+W\xba\x8a\x80\x90|\xd4\xa0\xd6\xa0\xd4T\xde\xe1\x9d,!\xe2\xb5\xa94\xe3\xe7\xd5\x9f\x06\x18\xcba\x03aP\xb8f\xcd\x04\x1a_\\9\xf1\xed\xe4\x9e\xe5\xa6\xd1\xd2\x9f\x03\xa7o\xae\x90H\xf3\xfb\xef\xffH3\xadk (esc)
969 969 \xb0\x90\x92\x88\xb9\x14"\x068\xc2\x1e@\x00\xbb\x8a)\xd3'\x859 (esc)
970 970 \xa8\x80\x84S \xa5\xbd-g\x13`\xe4\xdc\xc3H^\xdf\xe2\xc0TM\xc7\xf4BO\xcf\xde\xae\xe5\xae#\x1frM(K\x97`F\x19\x16s\x05GD\xb9\x01\xc1\x00+\x8c|\x9fp\xc11\xf0\x14\x00\x9cJ\x82<\xe0\x12\x9f\xc1\x90\xd0\xf5\xc8\x19>Pr\xaa\xeaW\xf5\xc4\xae\xd1\xfc\x17\xcf'\x13u\xb1\x9e\xcdHnC\x0e\xcc`\xc8\xa0&\xac\x0e\xf1|\x8c\x10$\xc4\x8c\xa2p\x05`\xdc\x08 \x80\xc4\xd7Rr-\x94\x10\x102\xedi;\xf3f\xf1z\x16\x86\xdb\xd8d\xe5\xe7\x8b\xf5\x8d\rzp\xb2\xfe\xac\xf5\xf2\xd3\xfe\xfckws\xedt\x96b\xd5l\x1c\x0b\x85\xb5\x170\x8f\x11\x84\xb0\x8f\x19\xa0\x00 _\x07\x1ac\xa2\xc3\x89Z\xe7\x96\xf9 \xccNFg\xc7F\xaa\x8a+\x9a\x9cc_\x17\x1b\x17\x9e]z38<\x97+\xb5,",\xc8\xc8?\\\x91\xff\x17.~U\x96\x97\xf5%\xdeN<\x8e\xf5\x97%\xe7^\xcfL\xed~\xda\x96k\xdc->\x86\x02\x83"\x96H\xa6\xe3\xaas=-\xeb7\xe5\xda\x8f\xbc (no-eol) (esc)
971 971
972 972 stream_out
973 973
974 974 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=stream_out'
975 975 200 Script output follows
976 976
977 977 1
978 978
979 979 failing unbundle, requires POST request
980 980
981 981 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=unbundle'
982 982 405 push requires POST request
983 983
984 984 0
985 985 push requires POST request
986 986 [1]
987 987
988 988 Static files
989 989
990 990 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/static/style.css'
991 991 200 Script output follows
992 992
993 993 a { text-decoration:none; }
994 994 .age { white-space:nowrap; }
995 995 .date { white-space:nowrap; }
996 996 .indexlinks { white-space:nowrap; }
997 997 .parity0 { background-color: #ddd; }
998 998 .parity1 { background-color: #eee; }
999 999 .lineno { width: 60px; color: #aaa; font-size: smaller;
1000 1000 text-align: right; }
1001 1001 .plusline { color: green; }
1002 1002 .minusline { color: red; }
1003 1003 .atline { color: purple; }
1004 1004 .annotate { font-size: smaller; text-align: right; padding-right: 1em; }
1005 1005 .buttons a {
1006 1006 background-color: #666;
1007 1007 padding: 2pt;
1008 1008 color: white;
1009 1009 font-family: sans;
1010 1010 font-weight: bold;
1011 1011 }
1012 1012 .navigate a {
1013 1013 background-color: #ccc;
1014 1014 padding: 2pt;
1015 1015 font-family: sans;
1016 1016 color: black;
1017 1017 }
1018 1018
1019 1019 .metatag {
1020 1020 background-color: #888;
1021 1021 color: white;
1022 1022 text-align: right;
1023 1023 }
1024 1024
1025 1025 /* Common */
1026 1026 pre { margin: 0; }
1027 1027
1028 1028 .logo {
1029 1029 float: right;
1030 1030 clear: right;
1031 1031 }
1032 1032
1033 1033 /* Changelog/Filelog entries */
1034 1034 .logEntry { width: 100%; }
1035 1035 .logEntry .age { width: 15%; }
1036 1036 .logEntry th { font-weight: normal; text-align: right; vertical-align: top; }
1037 1037 .logEntry th.age, .logEntry th.firstline { font-weight: bold; }
1038 1038 .logEntry th.firstline { text-align: left; width: inherit; }
1039 1039
1040 1040 /* Shortlog entries */
1041 1041 .slogEntry { width: 100%; }
1042 1042 .slogEntry .age { width: 8em; }
1043 1043 .slogEntry td { font-weight: normal; text-align: left; vertical-align: top; }
1044 1044 .slogEntry td.author { width: 15em; }
1045 1045
1046 1046 /* Tag entries */
1047 1047 #tagEntries { list-style: none; margin: 0; padding: 0; }
1048 1048 #tagEntries .tagEntry { list-style: none; margin: 0; padding: 0; }
1049 1049
1050 1050 /* Changeset entry */
1051 1051 #changesetEntry { }
1052 1052 #changesetEntry th { font-weight: normal; background-color: #888; color: #fff; text-align: right; }
1053 1053 #changesetEntry th.files, #changesetEntry th.description { vertical-align: top; }
1054 1054
1055 1055 /* File diff view */
1056 1056 #filediffEntry { }
1057 1057 #filediffEntry th { font-weight: normal; background-color: #888; color: #fff; text-align: right; }
1058 1058
1059 1059 /* Graph */
1060 1060 div#wrapper {
1061 1061 position: relative;
1062 1062 margin: 0;
1063 1063 padding: 0;
1064 1064 }
1065 1065
1066 1066 canvas {
1067 1067 position: absolute;
1068 1068 z-index: 5;
1069 1069 top: -0.6em;
1070 1070 margin: 0;
1071 1071 }
1072 1072
1073 1073 ul#nodebgs {
1074 1074 list-style: none inside none;
1075 1075 padding: 0;
1076 1076 margin: 0;
1077 1077 top: -0.7em;
1078 1078 }
1079 1079
1080 1080 ul#graphnodes li, ul#nodebgs li {
1081 1081 height: 39px;
1082 1082 }
1083 1083
1084 1084 ul#graphnodes {
1085 1085 position: absolute;
1086 1086 z-index: 10;
1087 1087 top: -0.85em;
1088 1088 list-style: none inside none;
1089 1089 padding: 0;
1090 1090 }
1091 1091
1092 1092 ul#graphnodes li .info {
1093 1093 display: block;
1094 1094 font-size: 70%;
1095 1095 position: relative;
1096 1096 top: -1px;
1097 1097 }
1098 1098
1099 1099 Stop and restart with HGENCODING=cp932
1100 1100
1101 1101 $ "$TESTDIR/killdaemons.py"
1102 1102 $ HGENCODING=cp932 hg serve --config server.uncompressed=False -n test \
1103 1103 > -p $HGPORT -d --pid-file=hg.pid -E errors.log
1104 1104 $ cat hg.pid >> $DAEMON_PIDS
1105 1105
1106 1106 commit message with Japanese Kanji 'Noh', which ends with '\x5c'
1107 1107
1108 1108 $ echo foo >> foo
1109 1109 $ HGENCODING=cp932 hg ci -m `python -c 'print("\x94\x5c")'`
1110 1110
1111 1111 Graph json escape of multibyte character
1112 1112
1113 1113 $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '/graph/' \
1114 1114 > | grep '^var data ='
1115 1115 var data = [["40b4d6888e92", [0, 1], [[0, 0, 1]], "\u80fd", "test", "1970-01-01", ["stable", true], ["tip"], ["something"]], ["1d22e65f027e", [0, 1], [[0, 0, 1]], "branch", "test", "1970-01-01", ["stable", false], [], []], ["a4f92ed23982", [0, 1], [[0, 0, 1]], "Added tag 1.0 for changeset 2ef0ac749a14", "test", "1970-01-01", ["default", true], [], []], ["2ef0ac749a14", [0, 1], [], "base", "test", "1970-01-01", ["default", false], ["1.0"], ["anotherthing"]]];
1116 1116
1117 1117 ERRORS ENCOUNTERED
1118 1118
1119 1119 $ cat errors.log
General Comments 0
You need to be logged in to leave comments. Login now