comparison hglib/client.py @ 142:fe74d5599539

hglib: wrap all application string literals in util.b() (issue4520) Conversion also included changing use of string interpolation to string concatenation as bytes interpolation does not exist in Python 3. Indexing related to bytes was also changed to length-1 bytes through slicing as Python 3 returns an int in this instance. Tests have not been switched to using util.b() so that the change to application code can be independently verified as not being broken.
author Brett Cannon <brett@python.org>
date Sun, 08 Mar 2015 13:08:37 -0400
parents dc63978871ed
children f3c430afa598
comparison
equal deleted inserted replaced
141:ea80bd2775f6 142:fe74d5599539
1 import subprocess, os, struct, cStringIO, re, datetime 1 import subprocess, os, struct, cStringIO, re, datetime
2 import hglib, error, util, templates, merge, context 2 import hglib, error, util, templates, merge, context
3 3
4 from util import cmdbuilder 4 from util import b, cmdbuilder
5 5
6 class revision(tuple): 6 class revision(tuple):
7 def __new__(cls, rev, node, tags, branch, author, desc, date): 7 def __new__(cls, rev, node, tags, branch, author, desc, date):
8 return tuple.__new__(cls, (rev, node, tags, branch, author, desc, date)) 8 return tuple.__new__(cls, (rev, node, tags, branch, author, desc, date))
9 9
70 self.close() 70 self.close()
71 71
72 def _readhello(self): 72 def _readhello(self):
73 """ read the hello message the server sends when started """ 73 """ read the hello message the server sends when started """
74 ch, msg = self._readchannel() 74 ch, msg = self._readchannel()
75 assert ch == 'o' 75 assert ch == b('o')
76 76
77 msg = msg.split('\n') 77 msg = msg.split(b('\n'))
78 78
79 self.capabilities = msg[0][len('capabilities: '):] 79 self.capabilities = msg[0][len(b('capabilities: ')):]
80 if not self.capabilities: 80 if not self.capabilities:
81 raise error.ResponseError( 81 raise error.ResponseError(
82 "bad hello message: expected 'capabilities: '" 82 "bad hello message: expected 'capabilities: '"
83 ", got %r" % msg[0]) 83 ", got %r" % msg[0])
84 84
85 self.capabilities = set(self.capabilities.split()) 85 self.capabilities = set(self.capabilities.split())
86 86
87 # at the very least the server should be able to run commands 87 # at the very least the server should be able to run commands
88 assert 'runcommand' in self.capabilities 88 assert b('runcommand') in self.capabilities
89 89
90 self._encoding = msg[1][len('encoding: '):] 90 self._encoding = msg[1][len(b('encoding: ')):]
91 if not self._encoding: 91 if not self._encoding:
92 raise error.ResponseError("bad hello message: expected 'encoding: '" 92 raise error.ResponseError("bad hello message: expected 'encoding: '"
93 ", got %r" % msg[1]) 93 ", got %r" % msg[1])
94 94
95 def _readchannel(self): 95 def _readchannel(self):
96 data = self.server.stdout.read(hgclient.outputfmtsize) 96 data = self.server.stdout.read(hgclient.outputfmtsize)
97 if not data: 97 if not data:
98 raise error.ServerError() 98 raise error.ServerError()
99 channel, length = struct.unpack(hgclient.outputfmt, data) 99 channel, length = struct.unpack(hgclient.outputfmt, data)
100 if channel in 'IL': 100 if channel in b('IL'):
101 return channel, length 101 return channel, length
102 else: 102 else:
103 return channel, self.server.stdout.read(length) 103 return channel, self.server.stdout.read(length)
104 104
105 @staticmethod 105 @staticmethod
108 each 6 fields compose one revision. 108 each 6 fields compose one revision.
109 ''' 109 '''
110 revs = [] 110 revs = []
111 for rev in util.grouper(7, splitted): 111 for rev in util.grouper(7, splitted):
112 # truncate the timezone and convert to a local datetime 112 # truncate the timezone and convert to a local datetime
113 posixtime = float(rev[6].split('.', 1)[0]) 113 posixtime = float(rev[6].split(b('.'), 1)[0])
114 dt = datetime.datetime.fromtimestamp(posixtime) 114 dt = datetime.datetime.fromtimestamp(posixtime)
115 revs.append(revision(rev[0], rev[1], rev[2], rev[3], 115 revs.append(revision(rev[0], rev[1], rev[2], rev[3],
116 rev[4], rev[5], dt)) 116 rev[4], rev[5], dt))
117 return revs 117 return revs
118 118
123 self.server.stdin.flush() 123 self.server.stdin.flush()
124 124
125 if not self.server: 125 if not self.server:
126 raise ValueError("server not connected") 126 raise ValueError("server not connected")
127 127
128 self.server.stdin.write('runcommand\n') 128 self.server.stdin.write(b('runcommand\n'))
129 writeblock('\0'.join(args)) 129 writeblock(b('\0').join(args))
130 130
131 while True: 131 while True:
132 channel, data = self._readchannel() 132 channel, data = self._readchannel()
133 133
134 # input channels 134 # input channels
136 writeblock(inchannels[channel](data)) 136 writeblock(inchannels[channel](data))
137 # output channels 137 # output channels
138 elif channel in outchannels: 138 elif channel in outchannels:
139 outchannels[channel](data) 139 outchannels[channel](data)
140 # result channel, command finished 140 # result channel, command finished
141 elif channel == 'r': 141 elif channel == b('r'):
142 return struct.unpack(hgclient.retfmt, data)[0] 142 return struct.unpack(hgclient.retfmt, data)[0]
143 # a channel that we don't know and can't ignore 143 # a channel that we don't know and can't ignore
144 elif channel.isupper(): 144 elif channel.isupper():
145 raise error.ResponseError( 145 raise error.ResponseError(
146 "unexpected data on required channel '%s'" % channel) 146 "unexpected data on required channel '%s'" % channel)
160 received so far 160 received so far
161 161
162 input is used to reply to bulk data requests by the server 162 input is used to reply to bulk data requests by the server
163 It receives the max number of bytes to return 163 It receives the max number of bytes to return
164 """ 164 """
165
166 out, err = cStringIO.StringIO(), cStringIO.StringIO() 165 out, err = cStringIO.StringIO(), cStringIO.StringIO()
167 outchannels = {'o' : out.write, 'e' : err.write} 166 outchannels = {b('o') : out.write, b('e') : err.write}
168 167
169 inchannels = {} 168 inchannels = {}
170 if prompt is not None: 169 if prompt is not None:
171 def func(size): 170 def func(size):
172 reply = prompt(size, out.getvalue()) 171 reply = prompt(size, out.getvalue())
173 return str(reply) 172 return reply
174 inchannels['L'] = func 173 inchannels[b('L')] = func
175 if input is not None: 174 if input is not None:
176 inchannels['I'] = input 175 inchannels[b('I')] = input
177 176
178 ret = self.runcommand(args, inchannels, outchannels) 177 ret = self.runcommand(args, inchannels, outchannels)
179 out, err = out.getvalue(), err.getvalue() 178 out, err = out.getvalue(), err.getvalue()
180 179
181 if ret: 180 if ret:
220 Return whether all given files were added. 219 Return whether all given files were added.
221 """ 220 """
222 if not isinstance(files, list): 221 if not isinstance(files, list):
223 files = [files] 222 files = [files]
224 223
225 args = cmdbuilder('add', n=dryrun, S=subrepos, I=include, X=exclude, 224 args = cmdbuilder(b('add'), n=dryrun, S=subrepos, I=include, X=exclude,
226 *files) 225 *files)
227 226
228 eh = util.reterrorhandler(args) 227 eh = util.reterrorhandler(args)
229 self.rawcommand(args, eh=eh) 228 self.rawcommand(args, eh=eh)
230 229
255 254
256 """ 255 """
257 if not isinstance(files, list): 256 if not isinstance(files, list):
258 files = [files] 257 files = [files]
259 258
260 args = cmdbuilder('addremove', s=similarity, n=dryrun, I=include, 259 args = cmdbuilder(b('addremove'), s=similarity, n=dryrun, I=include,
261 X=exclude, *files) 260 X=exclude, *files)
262 261
263 eh = util.reterrorhandler(args) 262 eh = util.reterrorhandler(args)
264 self.rawcommand(args, eh=eh) 263 self.rawcommand(args, eh=eh)
265 264
287 separated string according to the given options. 286 separated string according to the given options.
288 """ 287 """
289 if not isinstance(files, list): 288 if not isinstance(files, list):
290 files = [files] 289 files = [files]
291 290
292 args = cmdbuilder('annotate', r=rev, no_follow=nofollow, a=text, 291 args = cmdbuilder(b('annotate'), r=rev, no_follow=nofollow, a=text,
293 u=user, f=file, d=date, n=number, c=changeset, 292 u=user, f=file, d=date, n=number, c=changeset,
294 l=line, v=verbose, I=include, X=exclude, 293 l=line, v=verbose, I=include, X=exclude,
295 hidden=self.hidden, *files) 294 hidden=self.hidden, *files)
296 295
297 out = self.rawcommand(args) 296 out = self.rawcommand(args)
298 297
299 for line in out.splitlines(): 298 for line in out.splitlines():
300 yield tuple(line.split(': ', 1)) 299 yield tuple(line.split(b(': '), 1))
301 300
302 def archive(self, dest, rev=None, nodecode=False, prefix=None, type=None, 301 def archive(self, dest, rev=None, nodecode=False, prefix=None, type=None,
303 subrepos=False, include=None, exclude=None): 302 subrepos=False, include=None, exclude=None):
304 """Create an unversioned archive of a repository revision. 303 """Create an unversioned archive of a repository revision.
305 304
332 subrepos - recurse into subrepositories 331 subrepos - recurse into subrepositories
333 include - include names matching the given patterns 332 include - include names matching the given patterns
334 exclude - exclude names matching the given patterns 333 exclude - exclude names matching the given patterns
335 334
336 """ 335 """
337 args = cmdbuilder('archive', dest, r=rev, no_decode=nodecode, p=prefix, 336 args = cmdbuilder(b('archive'), dest, r=rev,
337 no_decode=nodecode, p=prefix,
338 t=type, S=subrepos, I=include, X=exclude, 338 t=type, S=subrepos, I=include, X=exclude,
339 hidden=self.hidden) 339 hidden=self.hidden)
340 340
341 self.rawcommand(args) 341 self.rawcommand(args)
342 342
360 360
361 """ 361 """
362 if message and logfile: 362 if message and logfile:
363 raise ValueError("cannot specify both a message and a logfile") 363 raise ValueError("cannot specify both a message and a logfile")
364 364
365 args = cmdbuilder('backout', r=rev, merge=merge, parent=parent, t=tool, 365 args = cmdbuilder(b('backout'), r=rev, merge=merge, parent=parent,
366 m=message, l=logfile, d=date, u=user, 366 t=tool, m=message, l=logfile, d=date, u=user,
367 hidden=self.hidden) 367 hidden=self.hidden)
368 368
369 self.rawcommand(args) 369 self.rawcommand(args)
370 370
371 def bookmark(self, name, rev=None, force=False, delete=False, 371 def bookmark(self, name, rev=None, force=False, delete=False,
379 force - bookmark even if another bookmark with the same name exists 379 force - bookmark even if another bookmark with the same name exists
380 delete - delete the given bookmark 380 delete - delete the given bookmark
381 inactive - do not mark the new bookmark active 381 inactive - do not mark the new bookmark active
382 rename - rename the bookmark given by rename to name 382 rename - rename the bookmark given by rename to name
383 """ 383 """
384 args = cmdbuilder('bookmark', name, r=rev, f=force, d=delete, 384 args = cmdbuilder(b('bookmark'), name, r=rev, f=force, d=delete,
385 i=inactive, m=rename) 385 i=inactive, m=rename)
386 386
387 self.rawcommand(args) 387 self.rawcommand(args)
388 388
389 def bookmarks(self): 389 def bookmarks(self):
391 Return the bookmarks as a list of (name, rev, node) and the index of the 391 Return the bookmarks as a list of (name, rev, node) and the index of the
392 current one. 392 current one.
393 393
394 If there isn't a current one, -1 is returned as the index. 394 If there isn't a current one, -1 is returned as the index.
395 """ 395 """
396 args = cmdbuilder('bookmarks', hidden=self.hidden) 396 args = cmdbuilder(b('bookmarks'), hidden=self.hidden)
397 out = self.rawcommand(args) 397 out = self.rawcommand(args)
398 398
399 bms = [] 399 bms = []
400 current = -1 400 current = -1
401 if out.rstrip() != 'no bookmarks set': 401 if out.rstrip() != b('no bookmarks set'):
402 for line in out.splitlines(): 402 for line in out.splitlines():
403 iscurrent, line = line[0:3], line[3:] 403 iscurrent, line = line[0:3], line[3:]
404 if '*' in iscurrent: 404 if b('*') in iscurrent:
405 current = len(bms) 405 current = len(bms)
406 name, line = line.split(' ', 1) 406 name, line = line.split(b(' '), 1)
407 rev, node = line.split(':') 407 rev, node = line.split(b(':'))
408 bms.append((name, int(rev), node)) 408 bms.append((name, int(rev), node))
409 return bms, current 409 return bms, current
410 410
411 def branch(self, name=None, clean=False, force=False): 411 def branch(self, name=None, clean=False, force=False):
412 """When name isn't given, return the current branch name. Otherwise 412 """When name isn't given, return the current branch name. Otherwise
425 425
426 """ 426 """
427 if name and clean: 427 if name and clean:
428 raise ValueError('cannot use both name and clean') 428 raise ValueError('cannot use both name and clean')
429 429
430 args = cmdbuilder('branch', name, f=force, C=clean) 430 args = cmdbuilder(b('branch'), name, f=force, C=clean)
431 out = self.rawcommand(args).rstrip() 431 out = self.rawcommand(args).rstrip()
432 432
433 if name: 433 if name:
434 return name 434 return name
435 elif not clean: 435 elif not clean:
443 Returns the repository's named branches as a list of (name, rev, node). 443 Returns the repository's named branches as a list of (name, rev, node).
444 444
445 active - show only branches that have unmerged heads 445 active - show only branches that have unmerged heads
446 closed - show normal and closed branches 446 closed - show normal and closed branches
447 """ 447 """
448 args = cmdbuilder('branches', a=active, c=closed, hidden=self.hidden) 448 args = cmdbuilder(b('branches'), a=active, c=closed, hidden=self.hidden)
449 out = self.rawcommand(args) 449 out = self.rawcommand(args)
450 450
451 branches = [] 451 branches = []
452 for line in out.rstrip().splitlines(): 452 for line in out.rstrip().splitlines():
453 namerev, node = line.rsplit(':', 1) 453 namerev, node = line.rsplit(b(':'), 1)
454 name, rev = namerev.rsplit(' ', 1) 454 name, rev = namerev.rsplit(b(' '), 1)
455 name = name.rstrip() 455 name = name.rstrip()
456 node = node.split()[0] # get rid of ' (inactive)' 456 node = node.split()[0] # get rid of ' (inactive)'
457 branches.append((name, int(rev), node)) 457 branches.append((name, int(rev), node))
458 return branches 458 return branches
459 459
483 web.cacerts config) 483 web.cacerts config)
484 484
485 Return True if a bundle was created, False if no changes were found. 485 Return True if a bundle was created, False if no changes were found.
486 486
487 """ 487 """
488 args = cmdbuilder('bundle', file, destrepo, f=force, r=rev, b=branch, 488 args = cmdbuilder(b('bundle'), file, destrepo, f=force, r=rev, b=branch,
489 base=base, a=all, t=type, e=ssh, remotecmd=remotecmd, 489 base=base, a=all, t=type, e=ssh, remotecmd=remotecmd,
490 insecure=insecure, hidden=self.hidden) 490 insecure=insecure, hidden=self.hidden)
491 491
492 eh = util.reterrorhandler(args) 492 eh = util.reterrorhandler(args)
493 self.rawcommand(args, eh=eh) 493 self.rawcommand(args, eh=eh)
507 "%s" basename of file being printed 507 "%s" basename of file being printed
508 "%d" dirname of file being printed, or '.' if in repository root 508 "%d" dirname of file being printed, or '.' if in repository root
509 "%p" root-relative path name of file being printed 509 "%p" root-relative path name of file being printed
510 510
511 """ 511 """
512 args = cmdbuilder('cat', r=rev, o=output, hidden=self.hidden, *files) 512 args = cmdbuilder(b('cat'), r=rev, o=output, hidden=self.hidden, *files)
513 out = self.rawcommand(args) 513 out = self.rawcommand(args)
514 514
515 if not output: 515 if not output:
516 return out 516 return out
517 517
518 def clone(self, source='.', dest=None, branch=None, updaterev=None, 518 def clone(self, source=b('.'), dest=None, branch=None, updaterev=None,
519 revrange=None): 519 revrange=None):
520 """ 520 """
521 Create a copy of an existing repository specified by source in a new 521 Create a copy of an existing repository specified by source in a new
522 directory dest. 522 directory dest.
523 523
525 525
526 branch - clone only the specified branch 526 branch - clone only the specified branch
527 updaterev - revision, tag or branch to check out 527 updaterev - revision, tag or branch to check out
528 revrange - include the specified changeset 528 revrange - include the specified changeset
529 """ 529 """
530 args = cmdbuilder('clone', source, dest, b=branch, 530 args = cmdbuilder(b('clone'), source, dest, b=branch,
531 u=updaterev, r=revrange) 531 u=updaterev, r=revrange)
532 self.rawcommand(args) 532 self.rawcommand(args)
533 533
534 def commit(self, message=None, logfile=None, addremove=False, 534 def commit(self, message=None, logfile=None, addremove=False,
535 closebranch=False, date=None, user=None, include=None, 535 closebranch=False, date=None, user=None, include=None,
547 exclude - exclude names matching the given patterns 547 exclude - exclude names matching the given patterns
548 amend - amend the parent of the working dir 548 amend - amend the parent of the working dir
549 """ 549 """
550 if amend and message is None and logfile is None: 550 if amend and message is None and logfile is None:
551 # retrieve current commit message 551 # retrieve current commit message
552 message = self.log('.')[0][5] 552 message = self.log(b('.'))[0][5]
553 if message is None and logfile is None and not amend: 553 if message is None and logfile is None and not amend:
554 raise ValueError("must provide at least a message or a logfile") 554 raise ValueError("must provide at least a message or a logfile")
555 elif message and logfile: 555 elif message and logfile:
556 raise ValueError("cannot specify both a message and a logfile") 556 raise ValueError("cannot specify both a message and a logfile")
557 557
558 # --debug will print the committed cset 558 # --debug will print the committed cset
559 args = cmdbuilder('commit', debug=True, m=message, A=addremove, 559 args = cmdbuilder(b('commit'), debug=True, m=message, A=addremove,
560 close_branch=closebranch, d=date, u=user, l=logfile, 560 close_branch=closebranch, d=date, u=user, l=logfile,
561 I=include, X=exclude, amend=amend) 561 I=include, X=exclude, amend=amend)
562 out = self.rawcommand(args) 562 out = self.rawcommand(args)
563 rev, node = out.splitlines()[-1].rsplit(':') 563 rev, node = out.splitlines()[-1].rsplit(b(':'))
564 return int(rev.split()[-1]), node 564 return int(rev.split()[-1]), node
565 565
566 def config(self, names=[], untrusted=False, showsource=False): 566 def config(self, names=[], untrusted=False, showsource=False):
567 """Return a list of (section, key, value) config settings from all 567 """Return a list of (section, key, value) config settings from all
568 hgrc files 568 hgrc files
570 When showsource is specified, return (source, section, key, value) where 570 When showsource is specified, return (source, section, key, value) where
571 source is of the form filename:[line] 571 source is of the form filename:[line]
572 572
573 """ 573 """
574 def splitline(s): 574 def splitline(s):
575 k, value = s.rstrip().split('=', 1) 575 k, value = s.rstrip().split(b('='), 1)
576 section, key = k.split('.', 1) 576 section, key = k.split(b('.'), 1)
577 return (section, key, value) 577 return section, key, value
578 578
579 if not isinstance(names, list): 579 if not isinstance(names, list):
580 names = [names] 580 names = [names]
581 581
582 args = cmdbuilder('showconfig', u=untrusted, debug=showsource, *names) 582 args = cmdbuilder(b('showconfig'), u=untrusted, debug=showsource,
583 *names)
583 out = self.rawcommand(args) 584 out = self.rawcommand(args)
584 585
585 conf = [] 586 conf = []
586 if showsource: 587 if showsource:
587 out = util.skiplines(out, 'read config from: ') 588 out = util.skiplines(out, b('read config from: '))
588 for line in out.splitlines(): 589 for line in out.splitlines():
589 m = re.match(r"(.+?:(?:\d+:)?) (.*)", line) 590 m = re.match(b(r"(.+?:(?:\d+:)?) (.*)"), line)
590 t = splitline(m.group(2)) 591 t = splitline(m.group(2))
591 conf.append((m.group(1)[:-1], t[0], t[1], t[2])) 592 conf.append((m.group(1)[:-1], t[0], t[1], t[2]))
592 else: 593 else:
593 for line in out.splitlines(): 594 for line in out.splitlines():
594 conf.append(splitline(line)) 595 conf.append(splitline(line))
598 @property 599 @property
599 def encoding(self): 600 def encoding(self):
600 """ 601 """
601 Return the server's encoding (as reported in the hello message). 602 Return the server's encoding (as reported in the hello message).
602 """ 603 """
603 if not 'getencoding' in self.capabilities: 604 if not b('getencoding') in self.capabilities:
604 raise CapabilityError('getencoding') 605 raise CapabilityError('getencoding')
605 606
606 if not self._encoding: 607 if not self._encoding:
607 self.server.stdin.write('getencoding\n') 608 self.server.stdin.write(b('getencoding\n'))
608 self._encoding = self._readfromchannel('r') 609 self._encoding = self._readfromchannel('r')
609 610
610 return self._encoding 611 return self._encoding
611 612
612 def copy(self, source, dest, after=False, force=False, dryrun=False, 613 def copy(self, source, dest, after=False, force=False, dryrun=False,
628 """ 629 """
629 if not isinstance(source, list): 630 if not isinstance(source, list):
630 source = [source] 631 source = [source]
631 632
632 source.append(dest) 633 source.append(dest)
633 args = cmdbuilder('copy', A=after, f=force, n=dryrun, 634 args = cmdbuilder(b('copy'), A=after, f=force, n=dryrun,
634 I=include, X=exclude, *source) 635 I=include, X=exclude, *source)
635 636
636 eh = util.reterrorhandler(args) 637 eh = util.reterrorhandler(args)
637 self.rawcommand(args, eh=eh) 638 self.rawcommand(args, eh=eh)
638 639
664 exclude - exclude names matching the given patterns 665 exclude - exclude names matching the given patterns
665 """ 666 """
666 if change and revs: 667 if change and revs:
667 raise ValueError('cannot specify both change and rev') 668 raise ValueError('cannot specify both change and rev')
668 669
669 args = cmdbuilder('diff', r=revs, c=change, 670 args = cmdbuilder(b('diff'), r=revs, c=change,
670 a=text, g=git, nodates=nodates, 671 a=text, g=git, nodates=nodates,
671 p=showfunction, reverse=reverse, 672 p=showfunction, reverse=reverse,
672 w=ignoreallspace, b=ignorespacechange, 673 w=ignoreallspace, b=ignorespacechange,
673 B=ignoreblanklines, U=unified, stat=stat, 674 B=ignoreblanklines, U=unified, stat=stat,
674 S=subrepos, I=include, X=exclude, hidden=self.hidden, 675 S=subrepos, I=include, X=exclude, hidden=self.hidden,
699 nodates - omit dates from diff headers 700 nodates - omit dates from diff headers
700 701
701 """ 702 """
702 if not isinstance(revs, list): 703 if not isinstance(revs, list):
703 revs = [revs] 704 revs = [revs]
704 args = cmdbuilder('export', o=output, switch_parent=switchparent, 705 args = cmdbuilder(b('export'), o=output, switch_parent=switchparent,
705 a=text, g=git, nodates=nodates, hidden=self.hidden, 706 a=text, g=git, nodates=nodates, hidden=self.hidden,
706 *revs) 707 *revs)
707 708
708 out = self.rawcommand(args) 709 out = self.rawcommand(args)
709 710
724 725
725 """ 726 """
726 if not isinstance(files, list): 727 if not isinstance(files, list):
727 files = [files] 728 files = [files]
728 729
729 args = cmdbuilder('forget', I=include, X=exclude, *files) 730 args = cmdbuilder(b('forget'), I=include, X=exclude, *files)
730 731
731 eh = util.reterrorhandler(args) 732 eh = util.reterrorhandler(args)
732 self.rawcommand(args, eh=eh) 733 self.rawcommand(args, eh=eh)
733 734
734 return bool(eh) 735 return bool(eh)
759 760
760 """ 761 """
761 if not isinstance(files, list): 762 if not isinstance(files, list):
762 files = [files] 763 files = [files]
763 764
764 args = cmdbuilder('grep', all=all, a=text, f=follow, i=ignorecase, 765 args = cmdbuilder(b('grep'), all=all, a=text, f=follow, i=ignorecase,
765 l=fileswithmatches, n=line, u=user, d=date, 766 l=fileswithmatches, n=line, u=user, d=date,
766 I=include, X=exclude, hidden=self.hidden, 767 I=include, X=exclude, hidden=self.hidden,
767 *[pattern] + files) 768 *[pattern] + files)
768 args.append('-0') 769 args.append(b('-0'))
769 770
770 def eh(ret, out, err): 771 def eh(ret, out, err):
771 if ret != 1: 772 if ret != 1:
772 raise error.CommandError(args, ret, out, err) 773 raise error.CommandError(args, ret, out, err)
773 return '' 774 return b('')
774 775
775 out = self.rawcommand(args, eh=eh).split('\0') 776 out = self.rawcommand(args, eh=eh).split(b('\0'))
776 777
777 fieldcount = 3 778 fieldcount = 3
778 if user: 779 if user:
779 fieldcount += 1 780 fieldcount += 1
780 if date: 781 if date:
802 803
803 """ 804 """
804 if not isinstance(rev, list): 805 if not isinstance(rev, list):
805 rev = [rev] 806 rev = [rev]
806 807
807 args = cmdbuilder('heads', r=startrev, t=topological, c=closed, 808 args = cmdbuilder(b('heads'), r=startrev, t=topological, c=closed,
808 template=templates.changeset, hidden=self.hidden, 809 template=templates.changeset, hidden=self.hidden,
809 *rev) 810 *rev)
810 811
811 def eh(ret, out, err): 812 def eh(ret, out, err):
812 if ret != 1: 813 if ret != 1:
813 raise error.CommandError(args, ret, out, err) 814 raise error.CommandError(args, ret, out, err)
814 return '' 815 return b('')
815 816
816 out = self.rawcommand(args, eh=eh).split('\0')[:-1] 817 out = self.rawcommand(args, eh=eh).split(b('\0'))[:-1]
817 return self._parserevs(out) 818 return self._parserevs(out)
818 819
819 def identify(self, rev=None, source=None, num=False, id=False, branch=False, 820 def identify(self, rev=None, source=None, num=False, id=False, branch=False,
820 tags=False, bookmarks=False): 821 tags=False, bookmarks=False):
821 """Return a summary string identifying the repository state at rev 822 """Return a summary string identifying the repository state at rev
834 branch - show branch 835 branch - show branch
835 tags - show tags 836 tags - show tags
836 bookmarks - show bookmarks 837 bookmarks - show bookmarks
837 838
838 """ 839 """
839 args = cmdbuilder('identify', source, r=rev, n=num, i=id, 840 args = cmdbuilder(b('identify'), source, r=rev, n=num, i=id,
840 b=branch, t=tags, B=bookmarks, 841 b=branch, t=tags, B=bookmarks,
841 hidden=self.hidden) 842 hidden=self.hidden)
842 843
843 return self.rawcommand(args) 844 return self.rawcommand(args)
844 845
876 else: 877 else:
877 stdin = False 878 stdin = False
878 prompt = None 879 prompt = None
879 input = None 880 input = None
880 881
881 args = cmdbuilder('import', strip=strip, force=force, 882 args = cmdbuilder(b('import'), strip=strip, force=force,
882 no_commit=nocommit, bypass=bypass, exact=exact, 883 no_commit=nocommit, bypass=bypass, exact=exact,
883 import_branch=importbranch, message=message, 884 import_branch=importbranch, message=message,
884 date=date, user=user, similarity=similarity, _=stdin, 885 date=date, user=user, similarity=similarity, _=stdin,
885 *patches) 886 *patches)
886 887
909 remotecmd - specify hg command to run on the remote side 910 remotecmd - specify hg command to run on the remote side
910 insecure- do not verify server certificate (ignoring web.cacerts config) 911 insecure- do not verify server certificate (ignoring web.cacerts config)
911 subrepos - recurse into subrepositories 912 subrepos - recurse into subrepositories
912 913
913 """ 914 """
914 args = cmdbuilder('incoming', path, 915 args = cmdbuilder(b('incoming'), path,
915 template=templates.changeset, r=revrange, 916 template=templates.changeset, r=revrange,
916 f=force, n=newest, bundle=bundle, 917 f=force, n=newest, bundle=bundle,
917 B=bookmarks, b=branch, l=limit, M=nomerges, 918 B=bookmarks, b=branch, l=limit, M=nomerges,
918 S=subrepos) 919 S=subrepos)
919 920
930 bms = [] 931 bms = []
931 for line in out.splitlines(): 932 for line in out.splitlines():
932 bms.append(tuple(line.split())) 933 bms.append(tuple(line.split()))
933 return bms 934 return bms
934 else: 935 else:
935 out = out.split('\0')[:-1] 936 out = out.split(b('\0'))[:-1]
936 return self._parserevs(out) 937 return self._parserevs(out)
937 938
938 def log(self, revrange=None, files=[], follow=False, 939 def log(self, revrange=None, files=[], follow=False,
939 followfirst=False, date=None, copies=False, keyword=None, 940 followfirst=False, date=None, copies=False, keyword=None,
940 removed=False, onlymerges=False, user=None, branch=None, 941 removed=False, onlymerges=False, user=None, branch=None,
981 exclude - exclude names matching the given patterns 982 exclude - exclude names matching the given patterns
982 983
983 """ 984 """
984 if hidden is None: 985 if hidden is None:
985 hidden = self.hidden 986 hidden = self.hidden
986 args = cmdbuilder('log', template=templates.changeset, 987 args = cmdbuilder(b('log'), template=templates.changeset,
987 r=revrange, f=follow, follow_first=followfirst, 988 r=revrange, f=follow, follow_first=followfirst,
988 d=date, C=copies, k=keyword, removed=removed, 989 d=date, C=copies, k=keyword, removed=removed,
989 m=onlymerges, u=user, b=branch, P=prune, 990 m=onlymerges, u=user, b=branch, P=prune,
990 l=limit, M=nomerges, I=include, X=exclude, 991 l=limit, M=nomerges, I=include, X=exclude,
991 hidden=hidden, *files) 992 hidden=hidden, *files)
992 993
993 out = self.rawcommand(args) 994 out = self.rawcommand(args)
994 out = out.split('\0')[:-1] 995 out = out.split(b('\0'))[:-1]
995 996
996 return self._parserevs(out) 997 return self._parserevs(out)
997 998
998 def manifest(self, rev=None, all=False): 999 def manifest(self, rev=None, all=False):
999 """Yields (nodeid, permission, executable, symlink, file path) tuples 1000 """Yields (nodeid, permission, executable, symlink, file path) tuples
1003 1004
1004 When all is True, all files from all revisions are yielded 1005 When all is True, all files from all revisions are yielded
1005 (just the name). This includes deleted and renamed files. 1006 (just the name). This includes deleted and renamed files.
1006 1007
1007 """ 1008 """
1008 args = cmdbuilder('manifest', r=rev, all=all, debug=True, 1009 args = cmdbuilder(b('manifest'), r=rev, all=all, debug=True,
1009 hidden=self.hidden) 1010 hidden=self.hidden)
1010 1011
1011 out = self.rawcommand(args) 1012 out = self.rawcommand(args)
1012 1013
1013 if all: 1014 if all:
1015 yield line 1016 yield line
1016 else: 1017 else:
1017 for line in out.splitlines(): 1018 for line in out.splitlines():
1018 node = line[0:40] 1019 node = line[0:40]
1019 perm = line[41:44] 1020 perm = line[41:44]
1020 symlink = line[45] == '@' 1021 symlink = line[45:46] == b('@')
1021 executable = line[45] == '*' 1022 executable = line[45:46] == b('*')
1022 yield (node, perm, executable, symlink, line[47:]) 1023 yield node, perm, executable, symlink, line[47:]
1023 1024
1024 def merge(self, rev=None, force=False, tool=None, cb=merge.handlers.abort): 1025 def merge(self, rev=None, force=False, tool=None, cb=merge.handlers.abort):
1025 """Merge working directory with rev. If no revision is specified, the 1026 """Merge working directory with rev. If no revision is specified, the
1026 working directory's parent is a head revision, and the current 1027 working directory's parent is a head revision, and the current
1027 branch contains exactly one other head, the other head is 1028 branch contains exactly one other head, the other head is
1045 which are the contents of stdout. It should return one of the 1046 which are the contents of stdout. It should return one of the
1046 expected choices (a single character). 1047 expected choices (a single character).
1047 1048
1048 """ 1049 """
1049 # we can't really use --preview since merge doesn't support --template 1050 # we can't really use --preview since merge doesn't support --template
1050 args = cmdbuilder('merge', r=rev, f=force, t=tool) 1051 args = cmdbuilder(b('merge'), r=rev, f=force, t=tool)
1051 1052
1052 prompt = None 1053 prompt = None
1053 if cb is merge.handlers.abort: 1054 if cb is merge.handlers.abort:
1054 prompt = cb 1055 prompt = cb
1055 elif cb is merge.handlers.noninteractive: 1056 elif cb is merge.handlers.noninteractive:
1056 args.append('-y') 1057 args.append(b('-y'))
1057 else: 1058 else:
1058 prompt = lambda size, output: cb(output) + '\n' 1059 prompt = lambda size, output: cb(output) + b('\n')
1059 1060
1060 self.rawcommand(args, prompt=prompt) 1061 self.rawcommand(args, prompt=prompt)
1061 1062
1062 def move(self, source, dest, after=False, force=False, dryrun=False, 1063 def move(self, source, dest, after=False, force=False, dryrun=False,
1063 include=None, exclude=None): 1064 include=None, exclude=None):
1078 """ 1079 """
1079 if not isinstance(source, list): 1080 if not isinstance(source, list):
1080 source = [source] 1081 source = [source]
1081 1082
1082 source.append(dest) 1083 source.append(dest)
1083 args = cmdbuilder('move', A=after, f=force, n=dryrun, 1084 args = cmdbuilder(b('move'), A=after, f=force, n=dryrun,
1084 I=include, X=exclude, *source) 1085 I=include, X=exclude, *source)
1085 1086
1086 eh = util.reterrorhandler(args) 1087 eh = util.reterrorhandler(args)
1087 self.rawcommand(args, eh=eh) 1088 self.rawcommand(args, eh=eh)
1088 1089
1106 the remote side insecure - do not verify server certificate 1107 the remote side insecure - do not verify server certificate
1107 (ignoring web.cacerts config) subrepos - recurse into 1108 (ignoring web.cacerts config) subrepos - recurse into
1108 subrepositories 1109 subrepositories
1109 1110
1110 """ 1111 """
1111 args = cmdbuilder('outgoing', 1112 args = cmdbuilder(b('outgoing'),
1112 path, 1113 path,
1113 template=templates.changeset, r=revrange, 1114 template=templates.changeset, r=revrange,
1114 f=force, n=newest, B=bookmarks, 1115 f=force, n=newest, B=bookmarks,
1115 b=branch, S=subrepos) 1116 b=branch, S=subrepos)
1116 1117
1127 bms = [] 1128 bms = []
1128 for line in out.splitlines(): 1129 for line in out.splitlines():
1129 bms.append(tuple(line.split())) 1130 bms.append(tuple(line.split()))
1130 return bms 1131 return bms
1131 else: 1132 else:
1132 out = out.split('\0')[:-1] 1133 out = out.split(b('\0'))[:-1]
1133 return self._parserevs(out) 1134 return self._parserevs(out)
1134 1135
1135 def parents(self, rev=None, file=None): 1136 def parents(self, rev=None, file=None):
1136 """Return the working directory's parent revisions. If rev is given, 1137 """Return the working directory's parent revisions. If rev is given,
1137 the parent of that revision will be printed. If file is given, 1138 the parent of that revision will be printed. If file is given,
1138 the revision in which the file was last changed (before the 1139 the revision in which the file was last changed (before the
1139 working directory revision or the revision specified by rev) 1140 working directory revision or the revision specified by rev)
1140 is returned. 1141 is returned.
1141 1142
1142 """ 1143 """
1143 args = cmdbuilder('parents', file, template=templates.changeset, r=rev, 1144 args = cmdbuilder(b('parents'), file, template=templates.changeset,
1144 hidden=self.hidden) 1145 r=rev, hidden=self.hidden)
1145 1146
1146 out = self.rawcommand(args) 1147 out = self.rawcommand(args)
1147 if not out: 1148 if not out:
1148 return 1149 return
1149 1150
1150 out = out.split('\0')[:-1] 1151 out = out.split(b('\0'))[:-1]
1151 1152
1152 return self._parserevs(out) 1153 return self._parserevs(out)
1153 1154
1154 def paths(self, name=None): 1155 def paths(self, name=None):
1155 """ 1156 """
1159 Path names are defined in the [paths] section of your configuration file 1160 Path names are defined in the [paths] section of your configuration file
1160 and in "/etc/mercurial/hgrc". If run inside a repository, ".hg/hgrc" is 1161 and in "/etc/mercurial/hgrc". If run inside a repository, ".hg/hgrc" is
1161 used, too. 1162 used, too.
1162 """ 1163 """
1163 if not name: 1164 if not name:
1164 out = self.rawcommand(['paths']) 1165 out = self.rawcommand([b('paths')])
1165 if not out: 1166 if not out:
1166 return {} 1167 return {}
1167 1168
1168 return dict([s.split(' = ') for s in out.rstrip().split('\n')]) 1169 return dict([s.split(b(' = '))
1170 for s in out.rstrip().split(b('\n'))])
1169 else: 1171 else:
1170 args = cmdbuilder('paths', name) 1172 args = cmdbuilder(b('paths'), name)
1171 out = self.rawcommand(args) 1173 out = self.rawcommand(args)
1172 return out.rstrip() 1174 return out.rstrip()
1173 1175
1174 def pull(self, source=None, rev=None, update=False, force=False, 1176 def pull(self, source=None, rev=None, update=False, force=False,
1175 bookmark=None, branch=None, ssh=None, remotecmd=None, 1177 bookmark=None, branch=None, ssh=None, remotecmd=None,
1194 insecure - do not verify server certificate (ignoring 1196 insecure - do not verify server certificate (ignoring
1195 web.cacerts config) 1197 web.cacerts config)
1196 tool - specify merge tool for rebase 1198 tool - specify merge tool for rebase
1197 1199
1198 """ 1200 """
1199 args = cmdbuilder('pull', source, r=rev, u=update, f=force, 1201 args = cmdbuilder(b('pull'), source, r=rev, u=update, f=force,
1200 B=bookmark, b=branch, e=ssh, 1202 B=bookmark, b=branch, e=ssh,
1201 remotecmd=remotecmd, insecure=insecure, 1203 remotecmd=remotecmd, insecure=insecure,
1202 t=tool) 1204 t=tool)
1203 1205
1204 eh = util.reterrorhandler(args) 1206 eh = util.reterrorhandler(args)
1231 remotecmd - specify hg command to run on the remote side 1233 remotecmd - specify hg command to run on the remote side
1232 insecure - do not verify server certificate (ignoring 1234 insecure - do not verify server certificate (ignoring
1233 web.cacerts config) 1235 web.cacerts config)
1234 1236
1235 """ 1237 """
1236 args = cmdbuilder('push', dest, r=rev, f=force, B=bookmark, b=branch, 1238 args = cmdbuilder(b('push'), dest, r=rev, f=force, B=bookmark, b=branch,
1237 new_branch=newbranch, e=ssh, remotecmd=remotecmd, 1239 new_branch=newbranch, e=ssh, remotecmd=remotecmd,
1238 insecure=insecure) 1240 insecure=insecure)
1239 1241
1240 eh = util.reterrorhandler(args) 1242 eh = util.reterrorhandler(args)
1241 self.rawcommand(args, eh=eh) 1243 self.rawcommand(args, eh=eh)
1257 1259
1258 """ 1260 """
1259 if not isinstance(files, list): 1261 if not isinstance(files, list):
1260 files = [files] 1262 files = [files]
1261 1263
1262 args = cmdbuilder('remove', A=after, f=force, I=include, X=exclude, 1264 args = cmdbuilder(b('remove'), A=after, f=force, I=include, X=exclude,
1263 *files) 1265 *files)
1264 1266
1265 eh = util.reterrorhandler(args) 1267 eh = util.reterrorhandler(args)
1266 self.rawcommand(args, eh=eh) 1268 self.rawcommand(args, eh=eh)
1267 1269
1285 exclude - exclude names matching the given patterns 1287 exclude - exclude names matching the given patterns
1286 """ 1288 """
1287 if not isinstance(file, list): 1289 if not isinstance(file, list):
1288 file = [file] 1290 file = [file]
1289 1291
1290 args = cmdbuilder('resolve', a=all, l=listfiles, m=mark, u=unmark, 1292 args = cmdbuilder(b('resolve'), a=all, l=listfiles, m=mark, u=unmark,
1291 t=tool, I=include, X=exclude, *file) 1293 t=tool, I=include, X=exclude, *file)
1292 1294
1293 out = self.rawcommand(args) 1295 out = self.rawcommand(args)
1294 1296
1295 if listfiles: 1297 if listfiles:
1296 l = [] 1298 l = []
1297 for line in out.splitlines(): 1299 for line in out.splitlines():
1298 l.append(tuple(line.split(' ', 1))) 1300 l.append(tuple(line.split(b(' '), 1)))
1299 return l 1301 return l
1300 1302
1301 def revert(self, files, rev=None, all=False, date=None, nobackup=False, 1303 def revert(self, files, rev=None, all=False, date=None, nobackup=False,
1302 dryrun=False, include=None, exclude=None): 1304 dryrun=False, include=None, exclude=None):
1303 """With no revision specified, revert the specified files or 1305 """With no revision specified, revert the specified files or
1328 1330
1329 """ 1331 """
1330 if not isinstance(files, list): 1332 if not isinstance(files, list):
1331 files = [files] 1333 files = [files]
1332 1334
1333 args = cmdbuilder('revert', r=rev, a=all, d=date, 1335 args = cmdbuilder(b('revert'), r=rev, a=all, d=date,
1334 no_backup=nobackup, n=dryrun, I=include, X=exclude, 1336 no_backup=nobackup, n=dryrun, I=include, X=exclude,
1335 hidden=self.hidden, *files) 1337 hidden=self.hidden, *files)
1336 1338
1337 eh = util.reterrorhandler(args) 1339 eh = util.reterrorhandler(args)
1338 self.rawcommand(args, eh=eh) 1340 self.rawcommand(args, eh=eh)
1341 1343
1342 def root(self): 1344 def root(self):
1343 """ 1345 """
1344 Return the root directory of the current repository. 1346 Return the root directory of the current repository.
1345 """ 1347 """
1346 return self.rawcommand(['root']).rstrip() 1348 return self.rawcommand([b('root')]).rstrip()
1347 1349
1348 def status(self, rev=None, change=None, all=False, modified=False, 1350 def status(self, rev=None, change=None, all=False, modified=False,
1349 added=False, removed=False, deleted=False, clean=False, 1351 added=False, removed=False, deleted=False, clean=False,
1350 unknown=False, ignored=False, copies=False, 1352 unknown=False, ignored=False, copies=False,
1351 subrepos=False, include=None, exclude=None): 1353 subrepos=False, include=None, exclude=None):
1378 exclude - exclude names matching the given patterns 1380 exclude - exclude names matching the given patterns
1379 """ 1381 """
1380 if rev and change: 1382 if rev and change:
1381 raise ValueError('cannot specify both rev and change') 1383 raise ValueError('cannot specify both rev and change')
1382 1384
1383 args = cmdbuilder('status', rev=rev, change=change, A=all, m=modified, 1385 args = cmdbuilder(b('status'), rev=rev, change=change, A=all,
1384 a=added, r=removed, d=deleted, c=clean, u=unknown, 1386 m=modified, a=added, r=removed, d=deleted, c=clean,
1385 i=ignored, C=copies, S=subrepos, I=include, 1387 u=unknown, i=ignored, C=copies, S=subrepos, I=include,
1386 X=exclude, hidden=self.hidden) 1388 X=exclude, hidden=self.hidden)
1387 1389
1388 args.append('-0') 1390 args.append(b('-0'))
1389 1391
1390 out = self.rawcommand(args) 1392 out = self.rawcommand(args)
1391 l = [] 1393 l = []
1392 1394
1393 for entry in out.split('\0'): 1395 for entry in out.split(b('\0')):
1394 if entry: 1396 if entry:
1395 if entry[0] == ' ': 1397 if entry[0:1] == b(' '):
1396 l.append((' ', entry[2:])) 1398 l.append((b(' '), entry[2:]))
1397 else: 1399 else:
1398 l.append(tuple(entry.split(' ', 1))) 1400 l.append(tuple(entry.split(b(' '), 1)))
1399 1401
1400 return l 1402 return l
1401 1403
1402 def tag(self, names, rev=None, message=None, force=False, local=False, 1404 def tag(self, names, rev=None, message=None, force=False, local=False,
1403 remove=False, date=None, user=None): 1405 remove=False, date=None, user=None):
1420 1422
1421 """ 1423 """
1422 if not isinstance(names, list): 1424 if not isinstance(names, list):
1423 names = [names] 1425 names = [names]
1424 1426
1425 args = cmdbuilder('tag', r=rev, m=message, f=force, l=local, 1427 args = cmdbuilder(b('tag'), r=rev, m=message, f=force, l=local,
1426 remove=remove, d=date, u=user, hidden=self.hidden, 1428 remove=remove, d=date, u=user, hidden=self.hidden,
1427 *names) 1429 *names)
1428 1430
1429 self.rawcommand(args) 1431 self.rawcommand(args)
1430 1432
1431 def tags(self): 1433 def tags(self):
1432 """ 1434 """
1433 Return a list of repository tags as: (name, rev, node, islocal) 1435 Return a list of repository tags as: (name, rev, node, islocal)
1434 """ 1436 """
1435 args = cmdbuilder('tags', v=True) 1437 args = cmdbuilder(b('tags'), v=True)
1436 1438
1437 out = self.rawcommand(args) 1439 out = self.rawcommand(args)
1438 1440
1439 t = [] 1441 t = []
1440 for line in out.splitlines(): 1442 for line in out.splitlines():
1441 taglocal = line.endswith(' local') 1443 taglocal = line.endswith(b(' local'))
1442 if taglocal: 1444 if taglocal:
1443 line = line[:-6] 1445 line = line[:-6]
1444 name, rev = line.rsplit(' ', 1) 1446 name, rev = line.rsplit(b(' '), 1)
1445 rev, node = rev.split(':') 1447 rev, node = rev.split(b(':'))
1446 t.append((name.rstrip(), int(rev), node, taglocal)) 1448 t.append((name.rstrip(), int(rev), node, taglocal))
1447 return t 1449 return t
1448 1450
1449 def phase(self, revs=(), secret=False, draft=False, public=False, 1451 def phase(self, revs=(), secret=False, draft=False, public=False,
1450 force=False): 1452 force=False):
1460 1462
1461 The arguments match the mercurial API. 1463 The arguments match the mercurial API.
1462 ''' 1464 '''
1463 if not isinstance(revs, (list, tuple)): 1465 if not isinstance(revs, (list, tuple)):
1464 revs = [revs] 1466 revs = [revs]
1465 args = util.cmdbuilder('phase', secret=secret, draft=draft, 1467 args = util.cmdbuilder(b('phase'), secret=secret, draft=draft,
1466 public=public, force=force, 1468 public=public, force=force,
1467 hidden=self.hidden, *revs) 1469 hidden=self.hidden, *revs)
1468 out = self.rawcommand(args) 1470 out = self.rawcommand(args)
1469 if draft or public or secret: 1471 if draft or public or secret:
1470 return 1472 return
1471 else: 1473 else:
1472 output = [i.split(': ')for i in out.strip().split('\n')] 1474 output = [i.split(b(': '))for i in out.strip().split(b('\n'))]
1473 return [(int(num), phase) for (num, phase) in output] 1475 return [(int(num), phase) for (num, phase) in output]
1474 1476
1475 def summary(self, remote=False): 1477 def summary(self, remote=False):
1476 """ 1478 """
1477 Return a dictionary with a brief summary of the working directory state, 1479 Return a dictionary with a brief summary of the working directory state,
1484 ['remote' : (in, in bookmarks, out, out bookmarks),] 1486 ['remote' : (in, in bookmarks, out, out bookmarks),]
1485 ['mq': (applied, unapplied) mq patches,] 1487 ['mq': (applied, unapplied) mq patches,]
1486 1488
1487 unparsed entries will be of them form key : value 1489 unparsed entries will be of them form key : value
1488 """ 1490 """
1489 args = cmdbuilder('summary', remote=remote, hidden=self.hidden) 1491 args = cmdbuilder(b('summary'), remote=remote, hidden=self.hidden)
1490 1492
1491 out = self.rawcommand(args).splitlines() 1493 out = self.rawcommand(args).splitlines()
1492 1494
1493 d = {} 1495 d = {}
1494 while out: 1496 while out:
1495 line = out.pop(0) 1497 line = out.pop(0)
1496 name, value = line.split(': ', 1) 1498 name, value = line.split(b(': '), 1)
1497 1499
1498 if name == 'parent': 1500 if name == b('parent'):
1499 parent, tags = value.split(' ', 1) 1501 parent, tags = value.split(b(' '), 1)
1500 rev, node = parent.split(':') 1502 rev, node = parent.split(b(':'))
1501 1503
1502 if tags: 1504 if tags:
1503 tags = tags.replace(' (empty repository)', '') 1505 tags = tags.replace(b(' (empty repository)'), b(''))
1504 else: 1506 else:
1505 tags = None 1507 tags = None
1506 1508
1507 value = d.get(name, []) 1509 value = d.get(name, [])
1508 1510
1509 if rev == '-1': 1511 if rev == b('-1'):
1510 value.append((int(rev), node, tags, None)) 1512 value.append((int(rev), node, tags, None))
1511 else: 1513 else:
1512 message = out.pop(0)[1:] 1514 message = out.pop(0)[1:]
1513 value.append((int(rev), node, tags, message)) 1515 value.append((int(rev), node, tags, message))
1514 elif name == 'branch': 1516 elif name == b('branch'):
1515 pass 1517 pass
1516 elif name == 'commit': 1518 elif name == b('commit'):
1517 value = value == '(clean)' 1519 value = value == b('(clean)')
1518 elif name == 'update': 1520 elif name == b('update'):
1519 if value == '(current)': 1521 if value == b('(current)'):
1520 value = 0 1522 value = 0
1521 else: 1523 else:
1522 value = int(value.split(' ', 1)[0]) 1524 value = int(value.split(b(' '), 1)[0])
1523 elif remote and name == 'remote': 1525 elif remote and name == b('remote'):
1524 if value == '(synced)': 1526 if value == b('(synced)'):
1525 value = 0, 0, 0, 0 1527 value = 0, 0, 0, 0
1526 else: 1528 else:
1527 inc = incb = out_ = outb = 0 1529 inc = incb = out_ = outb = 0
1528 1530
1529 for v in value.split(', '): 1531 for v in value.split(b(', ')):
1530 count, v = v.split(' ', 1) 1532 count, v = v.split(b(' '), 1)
1531 if v == 'outgoing': 1533 if v == b('outgoing'):
1532 out_ = int(count) 1534 out_ = int(count)
1533 elif v.endswith('incoming'): 1535 elif v.endswith(b('incoming')):
1534 inc = int(count) 1536 inc = int(count)
1535 elif v == 'incoming bookmarks': 1537 elif v == b('incoming bookmarks'):
1536 incb = int(count) 1538 incb = int(count)
1537 elif v == 'outgoing bookmarks': 1539 elif v == b('outgoing bookmarks'):
1538 outb = int(count) 1540 outb = int(count)
1539 1541
1540 value = inc, incb, out_, outb 1542 value = inc, incb, out_, outb
1541 elif name == 'mq': 1543 elif name == b('mq'):
1542 applied = unapplied = 0 1544 applied = unapplied = 0
1543 for v in value.split(', '): 1545 for v in value.split(b(', ')):
1544 count, v = v.split(' ', 1) 1546 count, v = v.split(b(' '), 1)
1545 if v == 'applied': 1547 if v == b('applied'):
1546 applied = int(count) 1548 applied = int(count)
1547 elif v == 'unapplied': 1549 elif v == b('unapplied'):
1548 unapplied = int(count) 1550 unapplied = int(count)
1549 value = applied, unapplied 1551 value = applied, unapplied
1550 1552
1551 d[name] = value 1553 d[name] = value
1552 1554
1556 """ 1558 """
1557 Return the tip revision (usually just called the tip) which is the 1559 Return the tip revision (usually just called the tip) which is the
1558 changeset most recently added to the repository (and therefore the most 1560 changeset most recently added to the repository (and therefore the most
1559 recently changed head). 1561 recently changed head).
1560 """ 1562 """
1561 args = cmdbuilder('tip', template=templates.changeset, 1563 args = cmdbuilder(b('tip'), template=templates.changeset,
1562 hidden=self.hidden) 1564 hidden=self.hidden)
1563 out = self.rawcommand(args) 1565 out = self.rawcommand(args)
1564 out = out.split('\0') 1566 out = out.split(b('\0'))
1565 1567
1566 return self._parserevs(out)[0] 1568 return self._parserevs(out)[0]
1567 1569
1568 def update(self, rev=None, clean=False, check=False, date=None): 1570 def update(self, rev=None, clean=False, check=False, date=None):
1569 """ 1571 """
1577 date - tipmost revision matching date 1579 date - tipmost revision matching date
1578 """ 1580 """
1579 if clean and check: 1581 if clean and check:
1580 raise ValueError('clean and check cannot both be True') 1582 raise ValueError('clean and check cannot both be True')
1581 1583
1582 args = cmdbuilder('update', r=rev, C=clean, c=check, d=date, 1584 args = cmdbuilder(b('update'), r=rev, C=clean, c=check, d=date,
1583 hidden=self.hidden) 1585 hidden=self.hidden)
1584 1586
1585 def eh(ret, out, err): 1587 def eh(ret, out, err):
1586 if ret == 1: 1588 if ret == 1:
1587 return out 1589 return out
1589 raise error.CommandError(args, ret, out, err) 1591 raise error.CommandError(args, ret, out, err)
1590 1592
1591 1593
1592 out = self.rawcommand(args, eh=eh) 1594 out = self.rawcommand(args, eh=eh)
1593 1595
1594 m = re.search(r'^(\d+).+, (\d+).+, (\d+).+, (\d+)', out, re.MULTILINE) 1596 m = re.search(b(r'^(\d+).+, (\d+).+, (\d+).+, (\d+)'), out,
1597 re.MULTILINE)
1595 return tuple(map(int, list(m.groups()))) 1598 return tuple(map(int, list(m.groups())))
1596 1599
1597 @property 1600 @property
1598 def version(self): 1601 def version(self):
1599 """Return hg version that runs the command server as a 4 fielded 1602 """Return hg version that runs the command server as a 4 fielded
1600 tuple: major, minor, micro and local build info. e.g. (1, 9, 1603 tuple: major, minor, micro and local build info. e.g. (1, 9,
1601 1, '+4-3095db9f5c2c') 1604 1, '+4-3095db9f5c2c')
1602 """ 1605 """
1603 if self._version is None: 1606 if self._version is None:
1604 v = self.rawcommand(cmdbuilder('version', q=True)) 1607 v = self.rawcommand(cmdbuilder(b('version'), q=True))
1605 v = list(re.match(r'.*?(\d+)\.(\d+)\.?(\d+)?(\+[0-9a-f-]+)?', 1608 v = list(re.match(b(r'.*?(\d+)\.(\d+)\.?(\d+)?(\+[0-9a-f-]+)?'),
1606 v).groups()) 1609 v).groups())
1607 1610
1608 for i in range(3): 1611 for i in range(3):
1609 try: 1612 try:
1610 v[i] = int(v[i]) 1613 v[i] = int(v[i])