##// END OF EJS Templates
wireproto: bypass filechunkiter for small files when streaming...
Bryan O'Sullivan -
r17557:6d97dd63 default
parent child Browse files
Show More
@@ -1,627 +1,630
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 peer, error, encoding, util, store
13 13 import discovery, phases
14 14
15 15 # abstract batching support
16 16
17 17 class future(object):
18 18 '''placeholder for a value to be set later'''
19 19 def set(self, value):
20 20 if util.safehasattr(self, 'value'):
21 21 raise error.RepoError("future is already set")
22 22 self.value = value
23 23
24 24 class batcher(object):
25 25 '''base class for batches of commands submittable in a single request
26 26
27 27 All methods invoked on instances of this class are simply queued and
28 28 return a a future for the result. Once you call submit(), all the queued
29 29 calls are performed and the results set in their respective futures.
30 30 '''
31 31 def __init__(self):
32 32 self.calls = []
33 33 def __getattr__(self, name):
34 34 def call(*args, **opts):
35 35 resref = future()
36 36 self.calls.append((name, args, opts, resref,))
37 37 return resref
38 38 return call
39 39 def submit(self):
40 40 pass
41 41
42 42 class localbatch(batcher):
43 43 '''performs the queued calls directly'''
44 44 def __init__(self, local):
45 45 batcher.__init__(self)
46 46 self.local = local
47 47 def submit(self):
48 48 for name, args, opts, resref in self.calls:
49 49 resref.set(getattr(self.local, name)(*args, **opts))
50 50
51 51 class remotebatch(batcher):
52 52 '''batches the queued calls; uses as few roundtrips as possible'''
53 53 def __init__(self, remote):
54 54 '''remote must support _submitbatch(encbatch) and
55 55 _submitone(op, encargs)'''
56 56 batcher.__init__(self)
57 57 self.remote = remote
58 58 def submit(self):
59 59 req, rsp = [], []
60 60 for name, args, opts, resref in self.calls:
61 61 mtd = getattr(self.remote, name)
62 62 batchablefn = getattr(mtd, 'batchable', None)
63 63 if batchablefn is not None:
64 64 batchable = batchablefn(mtd.im_self, *args, **opts)
65 65 encargsorres, encresref = batchable.next()
66 66 if encresref:
67 67 req.append((name, encargsorres,))
68 68 rsp.append((batchable, encresref, resref,))
69 69 else:
70 70 resref.set(encargsorres)
71 71 else:
72 72 if req:
73 73 self._submitreq(req, rsp)
74 74 req, rsp = [], []
75 75 resref.set(mtd(*args, **opts))
76 76 if req:
77 77 self._submitreq(req, rsp)
78 78 def _submitreq(self, req, rsp):
79 79 encresults = self.remote._submitbatch(req)
80 80 for encres, r in zip(encresults, rsp):
81 81 batchable, encresref, resref = r
82 82 encresref.set(encres)
83 83 resref.set(batchable.next())
84 84
85 85 def batchable(f):
86 86 '''annotation for batchable methods
87 87
88 88 Such methods must implement a coroutine as follows:
89 89
90 90 @batchable
91 91 def sample(self, one, two=None):
92 92 # Handle locally computable results first:
93 93 if not one:
94 94 yield "a local result", None
95 95 # Build list of encoded arguments suitable for your wire protocol:
96 96 encargs = [('one', encode(one),), ('two', encode(two),)]
97 97 # Create future for injection of encoded result:
98 98 encresref = future()
99 99 # Return encoded arguments and future:
100 100 yield encargs, encresref
101 101 # Assuming the future to be filled with the result from the batched
102 102 # request now. Decode it:
103 103 yield decode(encresref.value)
104 104
105 105 The decorator returns a function which wraps this coroutine as a plain
106 106 method, but adds the original method as an attribute called "batchable",
107 107 which is used by remotebatch to split the call into separate encoding and
108 108 decoding phases.
109 109 '''
110 110 def plain(*args, **opts):
111 111 batchable = f(*args, **opts)
112 112 encargsorres, encresref = batchable.next()
113 113 if not encresref:
114 114 return encargsorres # a local result in this case
115 115 self = args[0]
116 116 encresref.set(self._submitone(f.func_name, encargsorres))
117 117 return batchable.next()
118 118 setattr(plain, 'batchable', f)
119 119 return plain
120 120
121 121 # list of nodes encoding / decoding
122 122
123 123 def decodelist(l, sep=' '):
124 124 if l:
125 125 return map(bin, l.split(sep))
126 126 return []
127 127
128 128 def encodelist(l, sep=' '):
129 129 return sep.join(map(hex, l))
130 130
131 131 # batched call argument encoding
132 132
133 133 def escapearg(plain):
134 134 return (plain
135 135 .replace(':', '::')
136 136 .replace(',', ':,')
137 137 .replace(';', ':;')
138 138 .replace('=', ':='))
139 139
140 140 def unescapearg(escaped):
141 141 return (escaped
142 142 .replace(':=', '=')
143 143 .replace(':;', ';')
144 144 .replace(':,', ',')
145 145 .replace('::', ':'))
146 146
147 147 # client side
148 148
149 149 def todict(**args):
150 150 return args
151 151
152 152 class wirepeer(peer.peerrepository):
153 153
154 154 def batch(self):
155 155 return remotebatch(self)
156 156 def _submitbatch(self, req):
157 157 cmds = []
158 158 for op, argsdict in req:
159 159 args = ','.join('%s=%s' % p for p in argsdict.iteritems())
160 160 cmds.append('%s %s' % (op, args))
161 161 rsp = self._call("batch", cmds=';'.join(cmds))
162 162 return rsp.split(';')
163 163 def _submitone(self, op, args):
164 164 return self._call(op, **args)
165 165
166 166 @batchable
167 167 def lookup(self, key):
168 168 self.requirecap('lookup', _('look up remote revision'))
169 169 f = future()
170 170 yield todict(key=encoding.fromlocal(key)), f
171 171 d = f.value
172 172 success, data = d[:-1].split(" ", 1)
173 173 if int(success):
174 174 yield bin(data)
175 175 self._abort(error.RepoError(data))
176 176
177 177 @batchable
178 178 def heads(self):
179 179 f = future()
180 180 yield {}, f
181 181 d = f.value
182 182 try:
183 183 yield decodelist(d[:-1])
184 184 except ValueError:
185 185 self._abort(error.ResponseError(_("unexpected response:"), d))
186 186
187 187 @batchable
188 188 def known(self, nodes):
189 189 f = future()
190 190 yield todict(nodes=encodelist(nodes)), f
191 191 d = f.value
192 192 try:
193 193 yield [bool(int(f)) for f in d]
194 194 except ValueError:
195 195 self._abort(error.ResponseError(_("unexpected response:"), d))
196 196
197 197 @batchable
198 198 def branchmap(self):
199 199 f = future()
200 200 yield {}, f
201 201 d = f.value
202 202 try:
203 203 branchmap = {}
204 204 for branchpart in d.splitlines():
205 205 branchname, branchheads = branchpart.split(' ', 1)
206 206 branchname = encoding.tolocal(urllib.unquote(branchname))
207 207 branchheads = decodelist(branchheads)
208 208 branchmap[branchname] = branchheads
209 209 yield branchmap
210 210 except TypeError:
211 211 self._abort(error.ResponseError(_("unexpected response:"), d))
212 212
213 213 def branches(self, nodes):
214 214 n = encodelist(nodes)
215 215 d = self._call("branches", nodes=n)
216 216 try:
217 217 br = [tuple(decodelist(b)) for b in d.splitlines()]
218 218 return br
219 219 except ValueError:
220 220 self._abort(error.ResponseError(_("unexpected response:"), d))
221 221
222 222 def between(self, pairs):
223 223 batch = 8 # avoid giant requests
224 224 r = []
225 225 for i in xrange(0, len(pairs), batch):
226 226 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
227 227 d = self._call("between", pairs=n)
228 228 try:
229 229 r.extend(l and decodelist(l) or [] for l in d.splitlines())
230 230 except ValueError:
231 231 self._abort(error.ResponseError(_("unexpected response:"), d))
232 232 return r
233 233
234 234 @batchable
235 235 def pushkey(self, namespace, key, old, new):
236 236 if not self.capable('pushkey'):
237 237 yield False, None
238 238 f = future()
239 239 self.ui.debug('preparing pushkey for "%s:%s"\n' % (namespace, key))
240 240 yield todict(namespace=encoding.fromlocal(namespace),
241 241 key=encoding.fromlocal(key),
242 242 old=encoding.fromlocal(old),
243 243 new=encoding.fromlocal(new)), f
244 244 d = f.value
245 245 d, output = d.split('\n', 1)
246 246 try:
247 247 d = bool(int(d))
248 248 except ValueError:
249 249 raise error.ResponseError(
250 250 _('push failed (unexpected response):'), d)
251 251 for l in output.splitlines(True):
252 252 self.ui.status(_('remote: '), l)
253 253 yield d
254 254
255 255 @batchable
256 256 def listkeys(self, namespace):
257 257 if not self.capable('pushkey'):
258 258 yield {}, None
259 259 f = future()
260 260 self.ui.debug('preparing listkeys for "%s"\n' % namespace)
261 261 yield todict(namespace=encoding.fromlocal(namespace)), f
262 262 d = f.value
263 263 r = {}
264 264 for l in d.splitlines():
265 265 k, v = l.split('\t')
266 266 r[encoding.tolocal(k)] = encoding.tolocal(v)
267 267 yield r
268 268
269 269 def stream_out(self):
270 270 return self._callstream('stream_out')
271 271
272 272 def changegroup(self, nodes, kind):
273 273 n = encodelist(nodes)
274 274 f = self._callstream("changegroup", roots=n)
275 275 return changegroupmod.unbundle10(self._decompress(f), 'UN')
276 276
277 277 def changegroupsubset(self, bases, heads, kind):
278 278 self.requirecap('changegroupsubset', _('look up remote changes'))
279 279 bases = encodelist(bases)
280 280 heads = encodelist(heads)
281 281 f = self._callstream("changegroupsubset",
282 282 bases=bases, heads=heads)
283 283 return changegroupmod.unbundle10(self._decompress(f), 'UN')
284 284
285 285 def getbundle(self, source, heads=None, common=None):
286 286 self.requirecap('getbundle', _('look up remote changes'))
287 287 opts = {}
288 288 if heads is not None:
289 289 opts['heads'] = encodelist(heads)
290 290 if common is not None:
291 291 opts['common'] = encodelist(common)
292 292 f = self._callstream("getbundle", **opts)
293 293 return changegroupmod.unbundle10(self._decompress(f), 'UN')
294 294
295 295 def unbundle(self, cg, heads, source):
296 296 '''Send cg (a readable file-like object representing the
297 297 changegroup to push, typically a chunkbuffer object) to the
298 298 remote server as a bundle. Return an integer indicating the
299 299 result of the push (see localrepository.addchangegroup()).'''
300 300
301 301 if heads != ['force'] and self.capable('unbundlehash'):
302 302 heads = encodelist(['hashed',
303 303 util.sha1(''.join(sorted(heads))).digest()])
304 304 else:
305 305 heads = encodelist(heads)
306 306
307 307 ret, output = self._callpush("unbundle", cg, heads=heads)
308 308 if ret == "":
309 309 raise error.ResponseError(
310 310 _('push failed:'), output)
311 311 try:
312 312 ret = int(ret)
313 313 except ValueError:
314 314 raise error.ResponseError(
315 315 _('push failed (unexpected response):'), ret)
316 316
317 317 for l in output.splitlines(True):
318 318 self.ui.status(_('remote: '), l)
319 319 return ret
320 320
321 321 def debugwireargs(self, one, two, three=None, four=None, five=None):
322 322 # don't pass optional arguments left at their default value
323 323 opts = {}
324 324 if three is not None:
325 325 opts['three'] = three
326 326 if four is not None:
327 327 opts['four'] = four
328 328 return self._call('debugwireargs', one=one, two=two, **opts)
329 329
330 330 # server side
331 331
332 332 class streamres(object):
333 333 def __init__(self, gen):
334 334 self.gen = gen
335 335
336 336 class pushres(object):
337 337 def __init__(self, res):
338 338 self.res = res
339 339
340 340 class pusherr(object):
341 341 def __init__(self, res):
342 342 self.res = res
343 343
344 344 class ooberror(object):
345 345 def __init__(self, message):
346 346 self.message = message
347 347
348 348 def dispatch(repo, proto, command):
349 349 func, spec = commands[command]
350 350 args = proto.getargs(spec)
351 351 return func(repo, proto, *args)
352 352
353 353 def options(cmd, keys, others):
354 354 opts = {}
355 355 for k in keys:
356 356 if k in others:
357 357 opts[k] = others[k]
358 358 del others[k]
359 359 if others:
360 360 sys.stderr.write("abort: %s got unexpected arguments %s\n"
361 361 % (cmd, ",".join(others)))
362 362 return opts
363 363
364 364 def batch(repo, proto, cmds, others):
365 365 res = []
366 366 for pair in cmds.split(';'):
367 367 op, args = pair.split(' ', 1)
368 368 vals = {}
369 369 for a in args.split(','):
370 370 if a:
371 371 n, v = a.split('=')
372 372 vals[n] = unescapearg(v)
373 373 func, spec = commands[op]
374 374 if spec:
375 375 keys = spec.split()
376 376 data = {}
377 377 for k in keys:
378 378 if k == '*':
379 379 star = {}
380 380 for key in vals.keys():
381 381 if key not in keys:
382 382 star[key] = vals[key]
383 383 data['*'] = star
384 384 else:
385 385 data[k] = vals[k]
386 386 result = func(repo, proto, *[data[k] for k in keys])
387 387 else:
388 388 result = func(repo, proto)
389 389 if isinstance(result, ooberror):
390 390 return result
391 391 res.append(escapearg(result))
392 392 return ';'.join(res)
393 393
394 394 def between(repo, proto, pairs):
395 395 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
396 396 r = []
397 397 for b in repo.between(pairs):
398 398 r.append(encodelist(b) + "\n")
399 399 return "".join(r)
400 400
401 401 def branchmap(repo, proto):
402 402 branchmap = discovery.visiblebranchmap(repo)
403 403 heads = []
404 404 for branch, nodes in branchmap.iteritems():
405 405 branchname = urllib.quote(encoding.fromlocal(branch))
406 406 branchnodes = encodelist(nodes)
407 407 heads.append('%s %s' % (branchname, branchnodes))
408 408 return '\n'.join(heads)
409 409
410 410 def branches(repo, proto, nodes):
411 411 nodes = decodelist(nodes)
412 412 r = []
413 413 for b in repo.branches(nodes):
414 414 r.append(encodelist(b) + "\n")
415 415 return "".join(r)
416 416
417 417 def capabilities(repo, proto):
418 418 caps = ('lookup changegroupsubset branchmap pushkey known getbundle '
419 419 'unbundlehash batch').split()
420 420 if _allowstream(repo.ui):
421 421 if repo.ui.configbool('server', 'preferuncompressed', False):
422 422 caps.append('stream-preferred')
423 423 requiredformats = repo.requirements & repo.supportedformats
424 424 # if our local revlogs are just revlogv1, add 'stream' cap
425 425 if not requiredformats - set(('revlogv1',)):
426 426 caps.append('stream')
427 427 # otherwise, add 'streamreqs' detailing our local revlog format
428 428 else:
429 429 caps.append('streamreqs=%s' % ','.join(requiredformats))
430 430 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
431 431 caps.append('httpheader=1024')
432 432 return ' '.join(caps)
433 433
434 434 def changegroup(repo, proto, roots):
435 435 nodes = decodelist(roots)
436 436 cg = repo.changegroup(nodes, 'serve')
437 437 return streamres(proto.groupchunks(cg))
438 438
439 439 def changegroupsubset(repo, proto, bases, heads):
440 440 bases = decodelist(bases)
441 441 heads = decodelist(heads)
442 442 cg = repo.changegroupsubset(bases, heads, 'serve')
443 443 return streamres(proto.groupchunks(cg))
444 444
445 445 def debugwireargs(repo, proto, one, two, others):
446 446 # only accept optional args from the known set
447 447 opts = options('debugwireargs', ['three', 'four'], others)
448 448 return repo.debugwireargs(one, two, **opts)
449 449
450 450 def getbundle(repo, proto, others):
451 451 opts = options('getbundle', ['heads', 'common'], others)
452 452 for k, v in opts.iteritems():
453 453 opts[k] = decodelist(v)
454 454 cg = repo.getbundle('serve', **opts)
455 455 return streamres(proto.groupchunks(cg))
456 456
457 457 def heads(repo, proto):
458 458 h = discovery.visibleheads(repo)
459 459 return encodelist(h) + "\n"
460 460
461 461 def hello(repo, proto):
462 462 '''the hello command returns a set of lines describing various
463 463 interesting things about the server, in an RFC822-like format.
464 464 Currently the only one defined is "capabilities", which
465 465 consists of a line in the form:
466 466
467 467 capabilities: space separated list of tokens
468 468 '''
469 469 return "capabilities: %s\n" % (capabilities(repo, proto))
470 470
471 471 def listkeys(repo, proto, namespace):
472 472 d = repo.listkeys(encoding.tolocal(namespace)).items()
473 473 t = '\n'.join(['%s\t%s' % (encoding.fromlocal(k), encoding.fromlocal(v))
474 474 for k, v in d])
475 475 return t
476 476
477 477 def lookup(repo, proto, key):
478 478 try:
479 479 k = encoding.tolocal(key)
480 480 c = repo[k]
481 481 if c.phase() == phases.secret:
482 482 raise error.RepoLookupError(_("unknown revision '%s'") % k)
483 483 r = c.hex()
484 484 success = 1
485 485 except Exception, inst:
486 486 r = str(inst)
487 487 success = 0
488 488 return "%s %s\n" % (success, r)
489 489
490 490 def known(repo, proto, nodes, others):
491 491 return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
492 492
493 493 def pushkey(repo, proto, namespace, key, old, new):
494 494 # compatibility with pre-1.8 clients which were accidentally
495 495 # sending raw binary nodes rather than utf-8-encoded hex
496 496 if len(new) == 20 and new.encode('string-escape') != new:
497 497 # looks like it could be a binary node
498 498 try:
499 499 new.decode('utf-8')
500 500 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
501 501 except UnicodeDecodeError:
502 502 pass # binary, leave unmodified
503 503 else:
504 504 new = encoding.tolocal(new) # normal path
505 505
506 506 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
507 507 encoding.tolocal(old), new)
508 508 return '%s\n' % int(r)
509 509
510 510 def _allowstream(ui):
511 511 return ui.configbool('server', 'uncompressed', True, untrusted=True)
512 512
513 513 def stream(repo, proto):
514 514 '''If the server supports streaming clone, it advertises the "stream"
515 515 capability with a value representing the version and flags of the repo
516 516 it is serving. Client checks to see if it understands the format.
517 517
518 518 The format is simple: the server writes out a line with the amount
519 519 of files, then the total amount of bytes to be transferred (separated
520 520 by a space). Then, for each file, the server first writes the filename
521 521 and filesize (separated by the null character), then the file contents.
522 522 '''
523 523
524 524 if not _allowstream(repo.ui):
525 525 return '1\n'
526 526
527 527 entries = []
528 528 total_bytes = 0
529 529 try:
530 530 # get consistent snapshot of repo, lock during scan
531 531 lock = repo.lock()
532 532 try:
533 533 repo.ui.debug('scanning\n')
534 534 for name, ename, size in repo.store.walk():
535 535 entries.append((name, size))
536 536 total_bytes += size
537 537 finally:
538 538 lock.release()
539 539 except error.LockError:
540 540 return '2\n' # error: 2
541 541
542 542 def streamer(repo, entries, total):
543 543 '''stream out all metadata files in repository.'''
544 544 yield '0\n' # success
545 545 repo.ui.debug('%d files, %d bytes to transfer\n' %
546 546 (len(entries), total_bytes))
547 547 yield '%d %d\n' % (len(entries), total_bytes)
548 548
549 549 sopener = repo.sopener
550 550 oldaudit = sopener.mustaudit
551 551 sopener.mustaudit = False
552 552
553 553 try:
554 554 for name, size in entries:
555 555 repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
556 556 # partially encode name over the wire for backwards compat
557 557 yield '%s\0%d\n' % (store.encodedir(name), size)
558 for chunk in util.filechunkiter(sopener(name), limit=size):
559 yield chunk
558 if size <= 65536:
559 yield sopener(name).read(size)
560 else:
561 for chunk in util.filechunkiter(sopener(name), limit=size):
562 yield chunk
560 563 finally:
561 564 sopener.mustaudit = oldaudit
562 565
563 566 return streamres(streamer(repo, entries, total_bytes))
564 567
565 568 def unbundle(repo, proto, heads):
566 569 their_heads = decodelist(heads)
567 570
568 571 def check_heads():
569 572 heads = discovery.visibleheads(repo)
570 573 heads_hash = util.sha1(''.join(sorted(heads))).digest()
571 574 return (their_heads == ['force'] or their_heads == heads or
572 575 their_heads == ['hashed', heads_hash])
573 576
574 577 proto.redirect()
575 578
576 579 # fail early if possible
577 580 if not check_heads():
578 581 return pusherr('unsynced changes')
579 582
580 583 # write bundle data to temporary file because it can be big
581 584 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
582 585 fp = os.fdopen(fd, 'wb+')
583 586 r = 0
584 587 try:
585 588 proto.getfile(fp)
586 589 lock = repo.lock()
587 590 try:
588 591 if not check_heads():
589 592 # someone else committed/pushed/unbundled while we
590 593 # were transferring data
591 594 return pusherr('unsynced changes')
592 595
593 596 # push can proceed
594 597 fp.seek(0)
595 598 gen = changegroupmod.readbundle(fp, None)
596 599
597 600 try:
598 601 r = repo.addchangegroup(gen, 'serve', proto._client())
599 602 except util.Abort, inst:
600 603 sys.stderr.write("abort: %s\n" % inst)
601 604 finally:
602 605 lock.release()
603 606 return pushres(r)
604 607
605 608 finally:
606 609 fp.close()
607 610 os.unlink(tempname)
608 611
609 612 commands = {
610 613 'batch': (batch, 'cmds *'),
611 614 'between': (between, 'pairs'),
612 615 'branchmap': (branchmap, ''),
613 616 'branches': (branches, 'nodes'),
614 617 'capabilities': (capabilities, ''),
615 618 'changegroup': (changegroup, 'roots'),
616 619 'changegroupsubset': (changegroupsubset, 'bases heads'),
617 620 'debugwireargs': (debugwireargs, 'one two *'),
618 621 'getbundle': (getbundle, '*'),
619 622 'heads': (heads, ''),
620 623 'hello': (hello, ''),
621 624 'known': (known, 'nodes *'),
622 625 'listkeys': (listkeys, 'namespace'),
623 626 'lookup': (lookup, 'key'),
624 627 'pushkey': (pushkey, 'namespace key old new'),
625 628 'stream_out': (stream, ''),
626 629 'unbundle': (unbundle, 'heads'),
627 630 }
@@ -1,183 +1,190
1 1 $ check_code="$TESTDIR"/../contrib/check-code.py
2 2 $ cd "$TESTDIR"/..
3 3 $ if hg identify -q > /dev/null; then :
4 4 > else
5 5 > echo "skipped: not a Mercurial working dir" >&2
6 6 > exit 80
7 7 > fi
8 8 $ hg manifest | xargs "$check_code" || echo 'FAILURE IS NOT AN OPTION!!!'
9 mercurial/wireproto.py:560:
10 > yield sopener(name).read(size)
11 use opener.read() instead
12 FAILURE IS NOT AN OPTION!!!
9 13
10 14 $ hg manifest | xargs "$check_code" --warnings --nolineno --per-file=0 || true
11 15 hgext/convert/cvsps.py:0:
12 16 > ui.write('Ancestors: %s\n' % (','.join(r)))
13 17 warning: unwrapped ui message
14 18 hgext/convert/cvsps.py:0:
15 19 > ui.write('Parent: %d\n' % cs.parents[0].id)
16 20 warning: unwrapped ui message
17 21 hgext/convert/cvsps.py:0:
18 22 > ui.write('Parents: %s\n' %
19 23 warning: unwrapped ui message
20 24 hgext/convert/cvsps.py:0:
21 25 > ui.write('Branchpoints: %s \n' % ', '.join(branchpoints))
22 26 warning: unwrapped ui message
23 27 hgext/convert/cvsps.py:0:
24 28 > ui.write('Author: %s\n' % cs.author)
25 29 warning: unwrapped ui message
26 30 hgext/convert/cvsps.py:0:
27 31 > ui.write('Branch: %s\n' % (cs.branch or 'HEAD'))
28 32 warning: unwrapped ui message
29 33 hgext/convert/cvsps.py:0:
30 34 > ui.write('Date: %s\n' % util.datestr(cs.date,
31 35 warning: unwrapped ui message
32 36 hgext/convert/cvsps.py:0:
33 37 > ui.write('Log:\n')
34 38 warning: unwrapped ui message
35 39 hgext/convert/cvsps.py:0:
36 40 > ui.write('Members: \n')
37 41 warning: unwrapped ui message
38 42 hgext/convert/cvsps.py:0:
39 43 > ui.write('PatchSet %d \n' % cs.id)
40 44 warning: unwrapped ui message
41 45 hgext/convert/cvsps.py:0:
42 46 > ui.write('Tag%s: %s \n' % (['', 's'][len(cs.tags) > 1],
43 47 warning: unwrapped ui message
44 48 hgext/hgk.py:0:
45 49 > ui.write("parent %s\n" % p)
46 50 warning: unwrapped ui message
47 51 hgext/hgk.py:0:
48 52 > ui.write('k=%s\nv=%s\n' % (name, value))
49 53 warning: unwrapped ui message
50 54 hgext/hgk.py:0:
51 55 > ui.write("author %s %s %s\n" % (ctx.user(), int(date[0]), date[1]))
52 56 warning: unwrapped ui message
53 57 hgext/hgk.py:0:
54 58 > ui.write("branch %s\n\n" % ctx.branch())
55 59 warning: unwrapped ui message
56 60 hgext/hgk.py:0:
57 61 > ui.write("committer %s %s %s\n" % (committer, int(date[0]), date[1]))
58 62 warning: unwrapped ui message
59 63 hgext/hgk.py:0:
60 64 > ui.write("revision %d\n" % ctx.rev())
61 65 warning: unwrapped ui message
62 66 hgext/hgk.py:0:
63 67 > ui.write("tree %s\n" % short(ctx.changeset()[0]))
64 68 warning: unwrapped ui message
65 69 hgext/mq.py:0:
66 70 > ui.write("mq: %s\n" % ', '.join(m))
67 71 warning: unwrapped ui message
68 72 hgext/patchbomb.py:0:
69 73 > ui.write('Subject: %s\n' % subj)
70 74 warning: unwrapped ui message
71 75 hgext/patchbomb.py:0:
72 76 > ui.write('From: %s\n' % sender)
73 77 warning: unwrapped ui message
74 78 mercurial/commands.py:0:
75 79 > ui.note('branch %s\n' % data)
76 80 warning: unwrapped ui message
77 81 mercurial/commands.py:0:
78 82 > ui.note('node %s\n' % str(data))
79 83 warning: unwrapped ui message
80 84 mercurial/commands.py:0:
81 85 > ui.note('tag %s\n' % name)
82 86 warning: unwrapped ui message
83 87 mercurial/commands.py:0:
84 88 > ui.write("unpruned common: %s\n" % " ".join([short(n)
85 89 warning: unwrapped ui message
86 90 mercurial/commands.py:0:
87 91 > ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
88 92 warning: unwrapped ui message
89 93 mercurial/commands.py:0:
90 94 > ui.write("local is subset\n")
91 95 warning: unwrapped ui message
92 96 mercurial/commands.py:0:
93 97 > ui.write("remote is subset\n")
94 98 warning: unwrapped ui message
95 99 mercurial/commands.py:0:
96 100 > ui.write('deltas against other : ' + fmt % pcfmt(numother,
97 101 warning: unwrapped ui message
98 102 mercurial/commands.py:0:
99 103 > ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
100 104 warning: unwrapped ui message
101 105 mercurial/commands.py:0:
102 106 > ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
103 107 warning: unwrapped ui message
104 108 mercurial/commands.py:0:
105 109 > ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
106 110 warning: unwrapped ui message
107 111 mercurial/commands.py:0:
108 112 > ui.write("match: %s\n" % m(d[0]))
109 113 warning: unwrapped ui message
110 114 mercurial/commands.py:0:
111 115 > ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
112 116 warning: unwrapped ui message
113 117 mercurial/commands.py:0:
114 118 > ui.write('path %s\n' % k)
115 119 warning: unwrapped ui message
116 120 mercurial/commands.py:0:
117 121 > ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
118 122 warning: unwrapped ui message
119 123 mercurial/commands.py:0:
120 124 > ui.write("digraph G {\n")
121 125 warning: unwrapped ui message
122 126 mercurial/commands.py:0:
123 127 > ui.write("internal: %s %s\n" % d)
124 128 warning: unwrapped ui message
125 129 mercurial/commands.py:0:
126 130 > ui.write("standard: %s\n" % util.datestr(d))
127 131 warning: unwrapped ui message
128 132 mercurial/commands.py:0:
129 133 > ui.write('avg chain length : ' + fmt % avgchainlen)
130 134 warning: unwrapped ui message
131 135 mercurial/commands.py:0:
132 136 > ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
133 137 warning: unwrapped ui message
134 138 mercurial/commands.py:0:
135 139 > ui.write('compression ratio : ' + fmt % compratio)
136 140 warning: unwrapped ui message
137 141 mercurial/commands.py:0:
138 142 > ui.write('delta size (min/max/avg) : %d / %d / %d\n'
139 143 warning: unwrapped ui message
140 144 mercurial/commands.py:0:
141 145 > ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
142 146 warning: unwrapped ui message
143 147 mercurial/commands.py:0:
144 148 > ui.write('flags : %s\n' % ', '.join(flags))
145 149 warning: unwrapped ui message
146 150 mercurial/commands.py:0:
147 151 > ui.write('format : %d\n' % format)
148 152 warning: unwrapped ui message
149 153 mercurial/commands.py:0:
150 154 > ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
151 155 warning: unwrapped ui message
152 156 mercurial/commands.py:0:
153 157 > ui.write('revision size : ' + fmt2 % totalsize)
154 158 warning: unwrapped ui message
155 159 mercurial/commands.py:0:
156 160 > ui.write('revisions : ' + fmt2 % numrevs)
157 161 warning: unwrapped ui message
158 162 warning: unwrapped ui message
159 163 mercurial/commands.py:0:
160 164 > ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
161 165 warning: unwrapped ui message
166 mercurial/wireproto.py:0:
167 > yield sopener(name).read(size)
168 use opener.read() instead
162 169 tests/autodiff.py:0:
163 170 > ui.write('data lost for: %s\n' % fn)
164 171 warning: unwrapped ui message
165 172 tests/test-convert-mtn.t:0:
166 173 > > function get_passphrase(keypair_id)
167 174 don't use 'function', use old style
168 175 tests/test-import-git.t:0:
169 176 > > Mc\${NkU|\`?^000jF3jhEB
170 177 ^ must be quoted
171 178 tests/test-import.t:0:
172 179 > > diff -Naur proj-orig/foo proj-new/foo
173 180 don't use 'diff -N'
174 181 don't use 'diff -N'
175 182 tests/test-schemes.t:0:
176 183 > > z = file:\$PWD/
177 184 don't use $PWD, use `pwd`
178 185 tests/test-ui-color.py:0:
179 186 > testui.warn('warning\n')
180 187 warning: unwrapped ui message
181 188 tests/test-ui-color.py:0:
182 189 > testui.write('buffered\n')
183 190 warning: unwrapped ui message
General Comments 0
You need to be logged in to leave comments. Login now