##// END OF EJS Templates
ui: disable echo back of prompt input if ui is set to non-tty purposely...
Yuya Nishihara -
r22783:524b786b default
parent child Browse files
Show More
@@ -1,892 +1,894 b''
1 1 # ui.py - user interface bits for mercurial
2 2 #
3 3 # Copyright 2005-2007 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 from i18n import _
9 9 import errno, getpass, os, socket, sys, tempfile, traceback
10 10 import config, scmutil, util, error, formatter
11 11 from node import hex
12 12
13 13 samplehgrcs = {
14 14 'user':
15 15 """# example user config (see "hg help config" for more info)
16 16 [ui]
17 17 # name and email, e.g.
18 18 # username = Jane Doe <jdoe@example.com>
19 19 username =
20 20
21 21 [extensions]
22 22 # uncomment these lines to enable some popular extensions
23 23 # (see "hg help extensions" for more info)
24 24 #
25 25 # pager =
26 26 # progress =
27 27 # color =""",
28 28
29 29 'local':
30 30 """# example repository config (see "hg help config" for more info)
31 31 """,
32 32
33 33 'global':
34 34 """# example system-wide hg config (see "hg help config" for more info)
35 35
36 36 [extensions]
37 37 # uncomment these lines to enable some popular extensions
38 38 # (see "hg help extensions" for more info)
39 39 #
40 40 # blackbox =
41 41 # progress =
42 42 # color =
43 43 # pager =""",
44 44 }
45 45
46 46 class ui(object):
47 47 def __init__(self, src=None):
48 48 # _buffers: used for temporary capture of output
49 49 self._buffers = []
50 50 # _bufferstates: Should the temporary capture includes stderr
51 51 self._bufferstates = []
52 52 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
53 53 self._reportuntrusted = True
54 54 self._ocfg = config.config() # overlay
55 55 self._tcfg = config.config() # trusted
56 56 self._ucfg = config.config() # untrusted
57 57 self._trustusers = set()
58 58 self._trustgroups = set()
59 59 self.callhooks = True
60 60
61 61 if src:
62 62 self.fout = src.fout
63 63 self.ferr = src.ferr
64 64 self.fin = src.fin
65 65
66 66 self._tcfg = src._tcfg.copy()
67 67 self._ucfg = src._ucfg.copy()
68 68 self._ocfg = src._ocfg.copy()
69 69 self._trustusers = src._trustusers.copy()
70 70 self._trustgroups = src._trustgroups.copy()
71 71 self.environ = src.environ
72 72 self.callhooks = src.callhooks
73 73 self.fixconfig()
74 74 else:
75 75 self.fout = sys.stdout
76 76 self.ferr = sys.stderr
77 77 self.fin = sys.stdin
78 78
79 79 # shared read-only environment
80 80 self.environ = os.environ
81 81 # we always trust global config files
82 82 for f in scmutil.rcpath():
83 83 self.readconfig(f, trust=True)
84 84
85 85 def copy(self):
86 86 return self.__class__(self)
87 87
88 88 def formatter(self, topic, opts):
89 89 return formatter.formatter(self, topic, opts)
90 90
91 91 def _trusted(self, fp, f):
92 92 st = util.fstat(fp)
93 93 if util.isowner(st):
94 94 return True
95 95
96 96 tusers, tgroups = self._trustusers, self._trustgroups
97 97 if '*' in tusers or '*' in tgroups:
98 98 return True
99 99
100 100 user = util.username(st.st_uid)
101 101 group = util.groupname(st.st_gid)
102 102 if user in tusers or group in tgroups or user == util.username():
103 103 return True
104 104
105 105 if self._reportuntrusted:
106 106 self.warn(_('not trusting file %s from untrusted '
107 107 'user %s, group %s\n') % (f, user, group))
108 108 return False
109 109
110 110 def readconfig(self, filename, root=None, trust=False,
111 111 sections=None, remap=None):
112 112 try:
113 113 fp = open(filename)
114 114 except IOError:
115 115 if not sections: # ignore unless we were looking for something
116 116 return
117 117 raise
118 118
119 119 cfg = config.config()
120 120 trusted = sections or trust or self._trusted(fp, filename)
121 121
122 122 try:
123 123 cfg.read(filename, fp, sections=sections, remap=remap)
124 124 fp.close()
125 125 except error.ConfigError, inst:
126 126 if trusted:
127 127 raise
128 128 self.warn(_("ignored: %s\n") % str(inst))
129 129
130 130 if self.plain():
131 131 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
132 132 'logtemplate', 'style',
133 133 'traceback', 'verbose'):
134 134 if k in cfg['ui']:
135 135 del cfg['ui'][k]
136 136 for k, v in cfg.items('defaults'):
137 137 del cfg['defaults'][k]
138 138 # Don't remove aliases from the configuration if in the exceptionlist
139 139 if self.plain('alias'):
140 140 for k, v in cfg.items('alias'):
141 141 del cfg['alias'][k]
142 142
143 143 if trusted:
144 144 self._tcfg.update(cfg)
145 145 self._tcfg.update(self._ocfg)
146 146 self._ucfg.update(cfg)
147 147 self._ucfg.update(self._ocfg)
148 148
149 149 if root is None:
150 150 root = os.path.expanduser('~')
151 151 self.fixconfig(root=root)
152 152
153 153 def fixconfig(self, root=None, section=None):
154 154 if section in (None, 'paths'):
155 155 # expand vars and ~
156 156 # translate paths relative to root (or home) into absolute paths
157 157 root = root or os.getcwd()
158 158 for c in self._tcfg, self._ucfg, self._ocfg:
159 159 for n, p in c.items('paths'):
160 160 if not p:
161 161 continue
162 162 if '%%' in p:
163 163 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
164 164 % (n, p, self.configsource('paths', n)))
165 165 p = p.replace('%%', '%')
166 166 p = util.expandpath(p)
167 167 if not util.hasscheme(p) and not os.path.isabs(p):
168 168 p = os.path.normpath(os.path.join(root, p))
169 169 c.set("paths", n, p)
170 170
171 171 if section in (None, 'ui'):
172 172 # update ui options
173 173 self.debugflag = self.configbool('ui', 'debug')
174 174 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
175 175 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
176 176 if self.verbose and self.quiet:
177 177 self.quiet = self.verbose = False
178 178 self._reportuntrusted = self.debugflag or self.configbool("ui",
179 179 "report_untrusted", True)
180 180 self.tracebackflag = self.configbool('ui', 'traceback', False)
181 181
182 182 if section in (None, 'trusted'):
183 183 # update trust information
184 184 self._trustusers.update(self.configlist('trusted', 'users'))
185 185 self._trustgroups.update(self.configlist('trusted', 'groups'))
186 186
187 187 def backupconfig(self, section, item):
188 188 return (self._ocfg.backup(section, item),
189 189 self._tcfg.backup(section, item),
190 190 self._ucfg.backup(section, item),)
191 191 def restoreconfig(self, data):
192 192 self._ocfg.restore(data[0])
193 193 self._tcfg.restore(data[1])
194 194 self._ucfg.restore(data[2])
195 195
196 196 def setconfig(self, section, name, value, source=''):
197 197 for cfg in (self._ocfg, self._tcfg, self._ucfg):
198 198 cfg.set(section, name, value, source)
199 199 self.fixconfig(section=section)
200 200
201 201 def _data(self, untrusted):
202 202 return untrusted and self._ucfg or self._tcfg
203 203
204 204 def configsource(self, section, name, untrusted=False):
205 205 return self._data(untrusted).source(section, name) or 'none'
206 206
207 207 def config(self, section, name, default=None, untrusted=False):
208 208 if isinstance(name, list):
209 209 alternates = name
210 210 else:
211 211 alternates = [name]
212 212
213 213 for n in alternates:
214 214 value = self._data(untrusted).get(section, n, None)
215 215 if value is not None:
216 216 name = n
217 217 break
218 218 else:
219 219 value = default
220 220
221 221 if self.debugflag and not untrusted and self._reportuntrusted:
222 222 for n in alternates:
223 223 uvalue = self._ucfg.get(section, n)
224 224 if uvalue is not None and uvalue != value:
225 225 self.debug("ignoring untrusted configuration option "
226 226 "%s.%s = %s\n" % (section, n, uvalue))
227 227 return value
228 228
229 229 def configpath(self, section, name, default=None, untrusted=False):
230 230 'get a path config item, expanded relative to repo root or config file'
231 231 v = self.config(section, name, default, untrusted)
232 232 if v is None:
233 233 return None
234 234 if not os.path.isabs(v) or "://" not in v:
235 235 src = self.configsource(section, name, untrusted)
236 236 if ':' in src:
237 237 base = os.path.dirname(src.rsplit(':')[0])
238 238 v = os.path.join(base, os.path.expanduser(v))
239 239 return v
240 240
241 241 def configbool(self, section, name, default=False, untrusted=False):
242 242 """parse a configuration element as a boolean
243 243
244 244 >>> u = ui(); s = 'foo'
245 245 >>> u.setconfig(s, 'true', 'yes')
246 246 >>> u.configbool(s, 'true')
247 247 True
248 248 >>> u.setconfig(s, 'false', 'no')
249 249 >>> u.configbool(s, 'false')
250 250 False
251 251 >>> u.configbool(s, 'unknown')
252 252 False
253 253 >>> u.configbool(s, 'unknown', True)
254 254 True
255 255 >>> u.setconfig(s, 'invalid', 'somevalue')
256 256 >>> u.configbool(s, 'invalid')
257 257 Traceback (most recent call last):
258 258 ...
259 259 ConfigError: foo.invalid is not a boolean ('somevalue')
260 260 """
261 261
262 262 v = self.config(section, name, None, untrusted)
263 263 if v is None:
264 264 return default
265 265 if isinstance(v, bool):
266 266 return v
267 267 b = util.parsebool(v)
268 268 if b is None:
269 269 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
270 270 % (section, name, v))
271 271 return b
272 272
273 273 def configint(self, section, name, default=None, untrusted=False):
274 274 """parse a configuration element as an integer
275 275
276 276 >>> u = ui(); s = 'foo'
277 277 >>> u.setconfig(s, 'int1', '42')
278 278 >>> u.configint(s, 'int1')
279 279 42
280 280 >>> u.setconfig(s, 'int2', '-42')
281 281 >>> u.configint(s, 'int2')
282 282 -42
283 283 >>> u.configint(s, 'unknown', 7)
284 284 7
285 285 >>> u.setconfig(s, 'invalid', 'somevalue')
286 286 >>> u.configint(s, 'invalid')
287 287 Traceback (most recent call last):
288 288 ...
289 289 ConfigError: foo.invalid is not an integer ('somevalue')
290 290 """
291 291
292 292 v = self.config(section, name, None, untrusted)
293 293 if v is None:
294 294 return default
295 295 try:
296 296 return int(v)
297 297 except ValueError:
298 298 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
299 299 % (section, name, v))
300 300
301 301 def configbytes(self, section, name, default=0, untrusted=False):
302 302 """parse a configuration element as a quantity in bytes
303 303
304 304 Units can be specified as b (bytes), k or kb (kilobytes), m or
305 305 mb (megabytes), g or gb (gigabytes).
306 306
307 307 >>> u = ui(); s = 'foo'
308 308 >>> u.setconfig(s, 'val1', '42')
309 309 >>> u.configbytes(s, 'val1')
310 310 42
311 311 >>> u.setconfig(s, 'val2', '42.5 kb')
312 312 >>> u.configbytes(s, 'val2')
313 313 43520
314 314 >>> u.configbytes(s, 'unknown', '7 MB')
315 315 7340032
316 316 >>> u.setconfig(s, 'invalid', 'somevalue')
317 317 >>> u.configbytes(s, 'invalid')
318 318 Traceback (most recent call last):
319 319 ...
320 320 ConfigError: foo.invalid is not a byte quantity ('somevalue')
321 321 """
322 322
323 323 value = self.config(section, name)
324 324 if value is None:
325 325 if not isinstance(default, str):
326 326 return default
327 327 value = default
328 328 try:
329 329 return util.sizetoint(value)
330 330 except error.ParseError:
331 331 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
332 332 % (section, name, value))
333 333
334 334 def configlist(self, section, name, default=None, untrusted=False):
335 335 """parse a configuration element as a list of comma/space separated
336 336 strings
337 337
338 338 >>> u = ui(); s = 'foo'
339 339 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
340 340 >>> u.configlist(s, 'list1')
341 341 ['this', 'is', 'a small', 'test']
342 342 """
343 343
344 344 def _parse_plain(parts, s, offset):
345 345 whitespace = False
346 346 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
347 347 whitespace = True
348 348 offset += 1
349 349 if offset >= len(s):
350 350 return None, parts, offset
351 351 if whitespace:
352 352 parts.append('')
353 353 if s[offset] == '"' and not parts[-1]:
354 354 return _parse_quote, parts, offset + 1
355 355 elif s[offset] == '"' and parts[-1][-1] == '\\':
356 356 parts[-1] = parts[-1][:-1] + s[offset]
357 357 return _parse_plain, parts, offset + 1
358 358 parts[-1] += s[offset]
359 359 return _parse_plain, parts, offset + 1
360 360
361 361 def _parse_quote(parts, s, offset):
362 362 if offset < len(s) and s[offset] == '"': # ""
363 363 parts.append('')
364 364 offset += 1
365 365 while offset < len(s) and (s[offset].isspace() or
366 366 s[offset] == ','):
367 367 offset += 1
368 368 return _parse_plain, parts, offset
369 369
370 370 while offset < len(s) and s[offset] != '"':
371 371 if (s[offset] == '\\' and offset + 1 < len(s)
372 372 and s[offset + 1] == '"'):
373 373 offset += 1
374 374 parts[-1] += '"'
375 375 else:
376 376 parts[-1] += s[offset]
377 377 offset += 1
378 378
379 379 if offset >= len(s):
380 380 real_parts = _configlist(parts[-1])
381 381 if not real_parts:
382 382 parts[-1] = '"'
383 383 else:
384 384 real_parts[0] = '"' + real_parts[0]
385 385 parts = parts[:-1]
386 386 parts.extend(real_parts)
387 387 return None, parts, offset
388 388
389 389 offset += 1
390 390 while offset < len(s) and s[offset] in [' ', ',']:
391 391 offset += 1
392 392
393 393 if offset < len(s):
394 394 if offset + 1 == len(s) and s[offset] == '"':
395 395 parts[-1] += '"'
396 396 offset += 1
397 397 else:
398 398 parts.append('')
399 399 else:
400 400 return None, parts, offset
401 401
402 402 return _parse_plain, parts, offset
403 403
404 404 def _configlist(s):
405 405 s = s.rstrip(' ,')
406 406 if not s:
407 407 return []
408 408 parser, parts, offset = _parse_plain, [''], 0
409 409 while parser:
410 410 parser, parts, offset = parser(parts, s, offset)
411 411 return parts
412 412
413 413 result = self.config(section, name, untrusted=untrusted)
414 414 if result is None:
415 415 result = default or []
416 416 if isinstance(result, basestring):
417 417 result = _configlist(result.lstrip(' ,\n'))
418 418 if result is None:
419 419 result = default or []
420 420 return result
421 421
422 422 def has_section(self, section, untrusted=False):
423 423 '''tell whether section exists in config.'''
424 424 return section in self._data(untrusted)
425 425
426 426 def configitems(self, section, untrusted=False):
427 427 items = self._data(untrusted).items(section)
428 428 if self.debugflag and not untrusted and self._reportuntrusted:
429 429 for k, v in self._ucfg.items(section):
430 430 if self._tcfg.get(section, k) != v:
431 431 self.debug("ignoring untrusted configuration option "
432 432 "%s.%s = %s\n" % (section, k, v))
433 433 return items
434 434
435 435 def walkconfig(self, untrusted=False):
436 436 cfg = self._data(untrusted)
437 437 for section in cfg.sections():
438 438 for name, value in self.configitems(section, untrusted):
439 439 yield section, name, value
440 440
441 441 def plain(self, feature=None):
442 442 '''is plain mode active?
443 443
444 444 Plain mode means that all configuration variables which affect
445 445 the behavior and output of Mercurial should be
446 446 ignored. Additionally, the output should be stable,
447 447 reproducible and suitable for use in scripts or applications.
448 448
449 449 The only way to trigger plain mode is by setting either the
450 450 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
451 451
452 452 The return value can either be
453 453 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
454 454 - True otherwise
455 455 '''
456 456 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
457 457 return False
458 458 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
459 459 if feature and exceptions:
460 460 return feature not in exceptions
461 461 return True
462 462
463 463 def username(self):
464 464 """Return default username to be used in commits.
465 465
466 466 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
467 467 and stop searching if one of these is set.
468 468 If not found and ui.askusername is True, ask the user, else use
469 469 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
470 470 """
471 471 user = os.environ.get("HGUSER")
472 472 if user is None:
473 473 user = self.config("ui", ["username", "user"])
474 474 if user is not None:
475 475 user = os.path.expandvars(user)
476 476 if user is None:
477 477 user = os.environ.get("EMAIL")
478 478 if user is None and self.configbool("ui", "askusername"):
479 479 user = self.prompt(_("enter a commit username:"), default=None)
480 480 if user is None and not self.interactive():
481 481 try:
482 482 user = '%s@%s' % (util.getuser(), socket.getfqdn())
483 483 self.warn(_("no username found, using '%s' instead\n") % user)
484 484 except KeyError:
485 485 pass
486 486 if not user:
487 487 raise util.Abort(_('no username supplied'),
488 488 hint=_('use "hg config --edit" '
489 489 'to set your username'))
490 490 if "\n" in user:
491 491 raise util.Abort(_("username %s contains a newline\n") % repr(user))
492 492 return user
493 493
494 494 def shortuser(self, user):
495 495 """Return a short representation of a user name or email address."""
496 496 if not self.verbose:
497 497 user = util.shortuser(user)
498 498 return user
499 499
500 500 def expandpath(self, loc, default=None):
501 501 """Return repository location relative to cwd or from [paths]"""
502 502 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
503 503 return loc
504 504
505 505 path = self.config('paths', loc)
506 506 if not path and default is not None:
507 507 path = self.config('paths', default)
508 508 return path or loc
509 509
510 510 def pushbuffer(self, error=False):
511 511 """install a buffer to capture standar output of the ui object
512 512
513 513 If error is True, the error output will be captured too."""
514 514 self._buffers.append([])
515 515 self._bufferstates.append(error)
516 516
517 517 def popbuffer(self, labeled=False):
518 518 '''pop the last buffer and return the buffered output
519 519
520 520 If labeled is True, any labels associated with buffered
521 521 output will be handled. By default, this has no effect
522 522 on the output returned, but extensions and GUI tools may
523 523 handle this argument and returned styled output. If output
524 524 is being buffered so it can be captured and parsed or
525 525 processed, labeled should not be set to True.
526 526 '''
527 527 self._bufferstates.pop()
528 528 return "".join(self._buffers.pop())
529 529
530 530 def write(self, *args, **opts):
531 531 '''write args to output
532 532
533 533 By default, this method simply writes to the buffer or stdout,
534 534 but extensions or GUI tools may override this method,
535 535 write_err(), popbuffer(), and label() to style output from
536 536 various parts of hg.
537 537
538 538 An optional keyword argument, "label", can be passed in.
539 539 This should be a string containing label names separated by
540 540 space. Label names take the form of "topic.type". For example,
541 541 ui.debug() issues a label of "ui.debug".
542 542
543 543 When labeling output for a specific command, a label of
544 544 "cmdname.type" is recommended. For example, status issues
545 545 a label of "status.modified" for modified files.
546 546 '''
547 547 if self._buffers:
548 548 self._buffers[-1].extend([str(a) for a in args])
549 549 else:
550 550 for a in args:
551 551 self.fout.write(str(a))
552 552
553 553 def write_err(self, *args, **opts):
554 554 try:
555 555 if self._bufferstates and self._bufferstates[-1]:
556 556 return self.write(*args, **opts)
557 557 if not getattr(self.fout, 'closed', False):
558 558 self.fout.flush()
559 559 for a in args:
560 560 self.ferr.write(str(a))
561 561 # stderr may be buffered under win32 when redirected to files,
562 562 # including stdout.
563 563 if not getattr(self.ferr, 'closed', False):
564 564 self.ferr.flush()
565 565 except IOError, inst:
566 566 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
567 567 raise
568 568
569 569 def flush(self):
570 570 try: self.fout.flush()
571 571 except (IOError, ValueError): pass
572 572 try: self.ferr.flush()
573 573 except (IOError, ValueError): pass
574 574
575 575 def _isatty(self, fh):
576 576 if self.configbool('ui', 'nontty', False):
577 577 return False
578 578 return util.isatty(fh)
579 579
580 580 def interactive(self):
581 581 '''is interactive input allowed?
582 582
583 583 An interactive session is a session where input can be reasonably read
584 584 from `sys.stdin'. If this function returns false, any attempt to read
585 585 from stdin should fail with an error, unless a sensible default has been
586 586 specified.
587 587
588 588 Interactiveness is triggered by the value of the `ui.interactive'
589 589 configuration variable or - if it is unset - when `sys.stdin' points
590 590 to a terminal device.
591 591
592 592 This function refers to input only; for output, see `ui.formatted()'.
593 593 '''
594 594 i = self.configbool("ui", "interactive", None)
595 595 if i is None:
596 596 # some environments replace stdin without implementing isatty
597 597 # usually those are non-interactive
598 598 return self._isatty(self.fin)
599 599
600 600 return i
601 601
602 602 def termwidth(self):
603 603 '''how wide is the terminal in columns?
604 604 '''
605 605 if 'COLUMNS' in os.environ:
606 606 try:
607 607 return int(os.environ['COLUMNS'])
608 608 except ValueError:
609 609 pass
610 610 return util.termwidth()
611 611
612 612 def formatted(self):
613 613 '''should formatted output be used?
614 614
615 615 It is often desirable to format the output to suite the output medium.
616 616 Examples of this are truncating long lines or colorizing messages.
617 617 However, this is not often not desirable when piping output into other
618 618 utilities, e.g. `grep'.
619 619
620 620 Formatted output is triggered by the value of the `ui.formatted'
621 621 configuration variable or - if it is unset - when `sys.stdout' points
622 622 to a terminal device. Please note that `ui.formatted' should be
623 623 considered an implementation detail; it is not intended for use outside
624 624 Mercurial or its extensions.
625 625
626 626 This function refers to output only; for input, see `ui.interactive()'.
627 627 This function always returns false when in plain mode, see `ui.plain()'.
628 628 '''
629 629 if self.plain():
630 630 return False
631 631
632 632 i = self.configbool("ui", "formatted", None)
633 633 if i is None:
634 634 # some environments replace stdout without implementing isatty
635 635 # usually those are non-interactive
636 636 return self._isatty(self.fout)
637 637
638 638 return i
639 639
640 640 def _readline(self, prompt=''):
641 641 if self._isatty(self.fin):
642 642 try:
643 643 # magically add command line editing support, where
644 644 # available
645 645 import readline
646 646 # force demandimport to really load the module
647 647 readline.read_history_file
648 648 # windows sometimes raises something other than ImportError
649 649 except Exception:
650 650 pass
651 651
652 652 # call write() so output goes through subclassed implementation
653 653 # e.g. color extension on Windows
654 654 self.write(prompt)
655 655
656 656 # instead of trying to emulate raw_input, swap (self.fin,
657 657 # self.fout) with (sys.stdin, sys.stdout)
658 658 oldin = sys.stdin
659 659 oldout = sys.stdout
660 660 sys.stdin = self.fin
661 661 sys.stdout = self.fout
662 662 # prompt ' ' must exist; otherwise readline may delete entire line
663 663 # - http://bugs.python.org/issue12833
664 664 line = raw_input(' ')
665 665 sys.stdin = oldin
666 666 sys.stdout = oldout
667 667
668 668 # When stdin is in binary mode on Windows, it can cause
669 669 # raw_input() to emit an extra trailing carriage return
670 670 if os.linesep == '\r\n' and line and line[-1] == '\r':
671 671 line = line[:-1]
672 672 return line
673 673
674 674 def prompt(self, msg, default="y"):
675 675 """Prompt user with msg, read response.
676 676 If ui is not interactive, the default is returned.
677 677 """
678 678 if not self.interactive():
679 679 self.write(msg, ' ', default, "\n")
680 680 return default
681 681 try:
682 682 r = self._readline(self.label(msg, 'ui.prompt'))
683 683 if not r:
684 684 r = default
685 685 # sometimes self.interactive disagrees with isatty,
686 686 # show response provided on stdin when simulating
687 if not util.isatty(self.fin):
687 # but commandserver
688 if (not util.isatty(self.fin)
689 and not self.configbool('ui', 'nontty')):
688 690 self.write(r, "\n")
689 691 return r
690 692 except EOFError:
691 693 raise util.Abort(_('response expected'))
692 694
693 695 @staticmethod
694 696 def extractchoices(prompt):
695 697 """Extract prompt message and list of choices from specified prompt.
696 698
697 699 This returns tuple "(message, choices)", and "choices" is the
698 700 list of tuple "(response character, text without &)".
699 701 """
700 702 parts = prompt.split('$$')
701 703 msg = parts[0].rstrip(' ')
702 704 choices = [p.strip(' ') for p in parts[1:]]
703 705 return (msg,
704 706 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
705 707 for s in choices])
706 708
707 709 def promptchoice(self, prompt, default=0):
708 710 """Prompt user with a message, read response, and ensure it matches
709 711 one of the provided choices. The prompt is formatted as follows:
710 712
711 713 "would you like fries with that (Yn)? $$ &Yes $$ &No"
712 714
713 715 The index of the choice is returned. Responses are case
714 716 insensitive. If ui is not interactive, the default is
715 717 returned.
716 718 """
717 719
718 720 msg, choices = self.extractchoices(prompt)
719 721 resps = [r for r, t in choices]
720 722 while True:
721 723 r = self.prompt(msg, resps[default])
722 724 if r.lower() in resps:
723 725 return resps.index(r.lower())
724 726 self.write(_("unrecognized response\n"))
725 727
726 728 def getpass(self, prompt=None, default=None):
727 729 if not self.interactive():
728 730 return default
729 731 try:
730 732 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
731 733 # disable getpass() only if explicitly specified. it's still valid
732 734 # to interact with tty even if fin is not a tty.
733 735 if self.configbool('ui', 'nontty'):
734 736 return self.fin.readline().rstrip('\n')
735 737 else:
736 738 return getpass.getpass('')
737 739 except EOFError:
738 740 raise util.Abort(_('response expected'))
739 741 def status(self, *msg, **opts):
740 742 '''write status message to output (if ui.quiet is False)
741 743
742 744 This adds an output label of "ui.status".
743 745 '''
744 746 if not self.quiet:
745 747 opts['label'] = opts.get('label', '') + ' ui.status'
746 748 self.write(*msg, **opts)
747 749 def warn(self, *msg, **opts):
748 750 '''write warning message to output (stderr)
749 751
750 752 This adds an output label of "ui.warning".
751 753 '''
752 754 opts['label'] = opts.get('label', '') + ' ui.warning'
753 755 self.write_err(*msg, **opts)
754 756 def note(self, *msg, **opts):
755 757 '''write note to output (if ui.verbose is True)
756 758
757 759 This adds an output label of "ui.note".
758 760 '''
759 761 if self.verbose:
760 762 opts['label'] = opts.get('label', '') + ' ui.note'
761 763 self.write(*msg, **opts)
762 764 def debug(self, *msg, **opts):
763 765 '''write debug message to output (if ui.debugflag is True)
764 766
765 767 This adds an output label of "ui.debug".
766 768 '''
767 769 if self.debugflag:
768 770 opts['label'] = opts.get('label', '') + ' ui.debug'
769 771 self.write(*msg, **opts)
770 772 def edit(self, text, user, extra={}, editform=None):
771 773 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
772 774 text=True)
773 775 try:
774 776 f = os.fdopen(fd, "w")
775 777 f.write(text)
776 778 f.close()
777 779
778 780 environ = {'HGUSER': user}
779 781 if 'transplant_source' in extra:
780 782 environ.update({'HGREVISION': hex(extra['transplant_source'])})
781 783 for label in ('source', 'rebase_source'):
782 784 if label in extra:
783 785 environ.update({'HGREVISION': extra[label]})
784 786 break
785 787 if editform:
786 788 environ.update({'HGEDITFORM': editform})
787 789
788 790 editor = self.geteditor()
789 791
790 792 util.system("%s \"%s\"" % (editor, name),
791 793 environ=environ,
792 794 onerr=util.Abort, errprefix=_("edit failed"),
793 795 out=self.fout)
794 796
795 797 f = open(name)
796 798 t = f.read()
797 799 f.close()
798 800 finally:
799 801 os.unlink(name)
800 802
801 803 return t
802 804
803 805 def traceback(self, exc=None, force=False):
804 806 '''print exception traceback if traceback printing enabled or forced.
805 807 only to call in exception handler. returns true if traceback
806 808 printed.'''
807 809 if self.tracebackflag or force:
808 810 if exc is None:
809 811 exc = sys.exc_info()
810 812 cause = getattr(exc[1], 'cause', None)
811 813
812 814 if cause is not None:
813 815 causetb = traceback.format_tb(cause[2])
814 816 exctb = traceback.format_tb(exc[2])
815 817 exconly = traceback.format_exception_only(cause[0], cause[1])
816 818
817 819 # exclude frame where 'exc' was chained and rethrown from exctb
818 820 self.write_err('Traceback (most recent call last):\n',
819 821 ''.join(exctb[:-1]),
820 822 ''.join(causetb),
821 823 ''.join(exconly))
822 824 else:
823 825 traceback.print_exception(exc[0], exc[1], exc[2],
824 826 file=self.ferr)
825 827 return self.tracebackflag or force
826 828
827 829 def geteditor(self):
828 830 '''return editor to use'''
829 831 if sys.platform == 'plan9':
830 832 # vi is the MIPS instruction simulator on Plan 9. We
831 833 # instead default to E to plumb commit messages to
832 834 # avoid confusion.
833 835 editor = 'E'
834 836 else:
835 837 editor = 'vi'
836 838 return (os.environ.get("HGEDITOR") or
837 839 self.config("ui", "editor") or
838 840 os.environ.get("VISUAL") or
839 841 os.environ.get("EDITOR", editor))
840 842
841 843 def progress(self, topic, pos, item="", unit="", total=None):
842 844 '''show a progress message
843 845
844 846 With stock hg, this is simply a debug message that is hidden
845 847 by default, but with extensions or GUI tools it may be
846 848 visible. 'topic' is the current operation, 'item' is a
847 849 non-numeric marker of the current position (i.e. the currently
848 850 in-process file), 'pos' is the current numeric position (i.e.
849 851 revision, bytes, etc.), unit is a corresponding unit label,
850 852 and total is the highest expected pos.
851 853
852 854 Multiple nested topics may be active at a time.
853 855
854 856 All topics should be marked closed by setting pos to None at
855 857 termination.
856 858 '''
857 859
858 860 if pos is None or not self.debugflag:
859 861 return
860 862
861 863 if unit:
862 864 unit = ' ' + unit
863 865 if item:
864 866 item = ' ' + item
865 867
866 868 if total:
867 869 pct = 100.0 * pos / total
868 870 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
869 871 % (topic, item, pos, total, unit, pct))
870 872 else:
871 873 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
872 874
873 875 def log(self, service, *msg, **opts):
874 876 '''hook for logging facility extensions
875 877
876 878 service should be a readily-identifiable subsystem, which will
877 879 allow filtering.
878 880 message should be a newline-terminated string to log.
879 881 '''
880 882 pass
881 883
882 884 def label(self, msg, label):
883 885 '''style msg based on supplied label
884 886
885 887 Like ui.write(), this just returns msg unchanged, but extensions
886 888 and GUI tools can override it to allow styling output without
887 889 writing it.
888 890
889 891 ui.write(s, 'label') is equivalent to
890 892 ui.write(ui.label(s, 'label')).
891 893 '''
892 894 return msg
@@ -1,543 +1,551 b''
1 1 #if windows
2 2 $ PYTHONPATH="$TESTDIR/../contrib;$PYTHONPATH"
3 3 #else
4 4 $ PYTHONPATH="$TESTDIR/../contrib:$PYTHONPATH"
5 5 #endif
6 6 $ export PYTHONPATH
7 7
8 8 $ hg init repo
9 9 $ cd repo
10 10
11 11 >>> from hgclient import readchannel, runcommand, check
12 12 >>> @check
13 13 ... def hellomessage(server):
14 14 ... ch, data = readchannel(server)
15 15 ... print '%c, %r' % (ch, data)
16 16 ... # run an arbitrary command to make sure the next thing the server
17 17 ... # sends isn't part of the hello message
18 18 ... runcommand(server, ['id'])
19 19 o, 'capabilities: getencoding runcommand\nencoding: *' (glob)
20 20 *** runcommand id
21 21 000000000000 tip
22 22
23 23 >>> from hgclient import check
24 24 >>> @check
25 25 ... def unknowncommand(server):
26 26 ... server.stdin.write('unknowncommand\n')
27 27 abort: unknown command unknowncommand
28 28
29 29 >>> from hgclient import readchannel, runcommand, check
30 30 >>> @check
31 31 ... def checkruncommand(server):
32 32 ... # hello block
33 33 ... readchannel(server)
34 34 ...
35 35 ... # no args
36 36 ... runcommand(server, [])
37 37 ...
38 38 ... # global options
39 39 ... runcommand(server, ['id', '--quiet'])
40 40 ...
41 41 ... # make sure global options don't stick through requests
42 42 ... runcommand(server, ['id'])
43 43 ...
44 44 ... # --config
45 45 ... runcommand(server, ['id', '--config', 'ui.quiet=True'])
46 46 ...
47 47 ... # make sure --config doesn't stick
48 48 ... runcommand(server, ['id'])
49 49 ...
50 50 ... # negative return code should be masked
51 51 ... runcommand(server, ['id', '-runknown'])
52 52 *** runcommand
53 53 Mercurial Distributed SCM
54 54
55 55 basic commands:
56 56
57 57 add add the specified files on the next commit
58 58 annotate show changeset information by line for each file
59 59 clone make a copy of an existing repository
60 60 commit commit the specified files or all outstanding changes
61 61 diff diff repository (or selected files)
62 62 export dump the header and diffs for one or more changesets
63 63 forget forget the specified files on the next commit
64 64 init create a new repository in the given directory
65 65 log show revision history of entire repository or files
66 66 merge merge working directory with another revision
67 67 pull pull changes from the specified source
68 68 push push changes to the specified destination
69 69 remove remove the specified files on the next commit
70 70 serve start stand-alone webserver
71 71 status show changed files in the working directory
72 72 summary summarize working directory state
73 73 update update working directory (or switch revisions)
74 74
75 75 (use "hg help" for the full list of commands or "hg -v" for details)
76 76 *** runcommand id --quiet
77 77 000000000000
78 78 *** runcommand id
79 79 000000000000 tip
80 80 *** runcommand id --config ui.quiet=True
81 81 000000000000
82 82 *** runcommand id
83 83 000000000000 tip
84 84 *** runcommand id -runknown
85 85 abort: unknown revision 'unknown'!
86 86 [255]
87 87
88 88 >>> from hgclient import readchannel, check
89 89 >>> @check
90 90 ... def inputeof(server):
91 91 ... readchannel(server)
92 92 ... server.stdin.write('runcommand\n')
93 93 ... # close stdin while server is waiting for input
94 94 ... server.stdin.close()
95 95 ...
96 96 ... # server exits with 1 if the pipe closed while reading the command
97 97 ... print 'server exit code =', server.wait()
98 98 server exit code = 1
99 99
100 100 >>> import cStringIO
101 101 >>> from hgclient import readchannel, runcommand, check
102 102 >>> @check
103 103 ... def serverinput(server):
104 104 ... readchannel(server)
105 105 ...
106 106 ... patch = """
107 107 ... # HG changeset patch
108 108 ... # User test
109 109 ... # Date 0 0
110 110 ... # Node ID c103a3dec114d882c98382d684d8af798d09d857
111 111 ... # Parent 0000000000000000000000000000000000000000
112 112 ... 1
113 113 ...
114 114 ... diff -r 000000000000 -r c103a3dec114 a
115 115 ... --- /dev/null Thu Jan 01 00:00:00 1970 +0000
116 116 ... +++ b/a Thu Jan 01 00:00:00 1970 +0000
117 117 ... @@ -0,0 +1,1 @@
118 118 ... +1
119 119 ... """
120 120 ...
121 121 ... runcommand(server, ['import', '-'], input=cStringIO.StringIO(patch))
122 122 ... runcommand(server, ['log'])
123 123 *** runcommand import -
124 124 applying patch from stdin
125 125 *** runcommand log
126 126 changeset: 0:eff892de26ec
127 127 tag: tip
128 128 user: test
129 129 date: Thu Jan 01 00:00:00 1970 +0000
130 130 summary: 1
131 131
132 132
133 133 check that --cwd doesn't persist between requests:
134 134
135 135 $ mkdir foo
136 136 $ touch foo/bar
137 137 >>> from hgclient import readchannel, runcommand, check
138 138 >>> @check
139 139 ... def cwd(server):
140 140 ... readchannel(server)
141 141 ... runcommand(server, ['--cwd', 'foo', 'st', 'bar'])
142 142 ... runcommand(server, ['st', 'foo/bar'])
143 143 *** runcommand --cwd foo st bar
144 144 ? bar
145 145 *** runcommand st foo/bar
146 146 ? foo/bar
147 147
148 148 $ rm foo/bar
149 149
150 150
151 151 check that local configs for the cached repo aren't inherited when -R is used:
152 152
153 153 $ cat <<EOF >> .hg/hgrc
154 154 > [ui]
155 155 > foo = bar
156 156 > EOF
157 157
158 158 >>> from hgclient import readchannel, sep, runcommand, check
159 159 >>> @check
160 160 ... def localhgrc(server):
161 161 ... readchannel(server)
162 162 ...
163 163 ... # the cached repo local hgrc contains ui.foo=bar, so showconfig should
164 164 ... # show it
165 165 ... runcommand(server, ['showconfig'], outfilter=sep)
166 166 ...
167 167 ... # but not for this repo
168 168 ... runcommand(server, ['init', 'foo'])
169 169 ... runcommand(server, ['-R', 'foo', 'showconfig', 'ui', 'defaults'])
170 170 *** runcommand showconfig
171 171 bundle.mainreporoot=$TESTTMP/repo
172 172 defaults.backout=-d "0 0"
173 173 defaults.commit=-d "0 0"
174 174 defaults.shelve=--date "0 0"
175 175 defaults.tag=-d "0 0"
176 176 ui.slash=True
177 177 ui.interactive=False
178 178 ui.mergemarkers=detailed
179 179 ui.foo=bar
180 180 ui.nontty=true
181 181 *** runcommand init foo
182 182 *** runcommand -R foo showconfig ui defaults
183 183 defaults.backout=-d "0 0"
184 184 defaults.commit=-d "0 0"
185 185 defaults.shelve=--date "0 0"
186 186 defaults.tag=-d "0 0"
187 187 ui.slash=True
188 188 ui.interactive=False
189 189 ui.mergemarkers=detailed
190 190 ui.nontty=true
191 191
192 192 $ rm -R foo
193 193
194 194 #if windows
195 195 $ PYTHONPATH="$TESTTMP/repo;$PYTHONPATH"
196 196 #else
197 197 $ PYTHONPATH="$TESTTMP/repo:$PYTHONPATH"
198 198 #endif
199 199
200 200 $ cat <<EOF > hook.py
201 201 > import sys
202 202 > def hook(**args):
203 203 > print 'hook talking'
204 204 > print 'now try to read something: %r' % sys.stdin.read()
205 205 > EOF
206 206
207 207 >>> import cStringIO
208 208 >>> from hgclient import readchannel, runcommand, check
209 209 >>> @check
210 210 ... def hookoutput(server):
211 211 ... readchannel(server)
212 212 ... runcommand(server, ['--config',
213 213 ... 'hooks.pre-identify=python:hook.hook',
214 214 ... 'id'],
215 215 ... input=cStringIO.StringIO('some input'))
216 216 *** runcommand --config hooks.pre-identify=python:hook.hook id
217 217 hook talking
218 218 now try to read something: 'some input'
219 219 eff892de26ec tip
220 220
221 221 $ rm hook.py*
222 222
223 223 $ echo a >> a
224 224 >>> import os
225 225 >>> from hgclient import readchannel, runcommand, check
226 226 >>> @check
227 227 ... def outsidechanges(server):
228 228 ... readchannel(server)
229 229 ... runcommand(server, ['status'])
230 230 ... os.system('hg ci -Am2')
231 231 ... runcommand(server, ['tip'])
232 232 ... runcommand(server, ['status'])
233 233 *** runcommand status
234 234 M a
235 235 *** runcommand tip
236 236 changeset: 1:d3a0a68be6de
237 237 tag: tip
238 238 user: test
239 239 date: Thu Jan 01 00:00:00 1970 +0000
240 240 summary: 2
241 241
242 242 *** runcommand status
243 243
244 244 >>> import os
245 245 >>> from hgclient import readchannel, runcommand, check
246 246 >>> @check
247 247 ... def bookmarks(server):
248 248 ... readchannel(server)
249 249 ... runcommand(server, ['bookmarks'])
250 250 ...
251 251 ... # changes .hg/bookmarks
252 252 ... os.system('hg bookmark -i bm1')
253 253 ... os.system('hg bookmark -i bm2')
254 254 ... runcommand(server, ['bookmarks'])
255 255 ...
256 256 ... # changes .hg/bookmarks.current
257 257 ... os.system('hg upd bm1 -q')
258 258 ... runcommand(server, ['bookmarks'])
259 259 ...
260 260 ... runcommand(server, ['bookmarks', 'bm3'])
261 261 ... f = open('a', 'ab')
262 262 ... f.write('a\n')
263 263 ... f.close()
264 264 ... runcommand(server, ['commit', '-Amm'])
265 265 ... runcommand(server, ['bookmarks'])
266 266 *** runcommand bookmarks
267 267 no bookmarks set
268 268 *** runcommand bookmarks
269 269 bm1 1:d3a0a68be6de
270 270 bm2 1:d3a0a68be6de
271 271 *** runcommand bookmarks
272 272 * bm1 1:d3a0a68be6de
273 273 bm2 1:d3a0a68be6de
274 274 *** runcommand bookmarks bm3
275 275 *** runcommand commit -Amm
276 276 *** runcommand bookmarks
277 277 bm1 1:d3a0a68be6de
278 278 bm2 1:d3a0a68be6de
279 279 * bm3 2:aef17e88f5f0
280 280
281 281 >>> import os
282 282 >>> from hgclient import readchannel, runcommand, check
283 283 >>> @check
284 284 ... def tagscache(server):
285 285 ... readchannel(server)
286 286 ... runcommand(server, ['id', '-t', '-r', '0'])
287 287 ... os.system('hg tag -r 0 foo')
288 288 ... runcommand(server, ['id', '-t', '-r', '0'])
289 289 *** runcommand id -t -r 0
290 290
291 291 *** runcommand id -t -r 0
292 292 foo
293 293
294 294 >>> import os
295 295 >>> from hgclient import readchannel, runcommand, check
296 296 >>> @check
297 297 ... def setphase(server):
298 298 ... readchannel(server)
299 299 ... runcommand(server, ['phase', '-r', '.'])
300 300 ... os.system('hg phase -r . -p')
301 301 ... runcommand(server, ['phase', '-r', '.'])
302 302 *** runcommand phase -r .
303 303 3: draft
304 304 *** runcommand phase -r .
305 305 3: public
306 306
307 307 $ echo a >> a
308 308 >>> from hgclient import readchannel, runcommand, check
309 309 >>> @check
310 310 ... def rollback(server):
311 311 ... readchannel(server)
312 312 ... runcommand(server, ['phase', '-r', '.', '-p'])
313 313 ... runcommand(server, ['commit', '-Am.'])
314 314 ... runcommand(server, ['rollback'])
315 315 ... runcommand(server, ['phase', '-r', '.'])
316 316 *** runcommand phase -r . -p
317 317 no phases changed
318 318 [1]
319 319 *** runcommand commit -Am.
320 320 *** runcommand rollback
321 321 repository tip rolled back to revision 3 (undo commit)
322 322 working directory now based on revision 3
323 323 *** runcommand phase -r .
324 324 3: public
325 325
326 326 >>> import os
327 327 >>> from hgclient import readchannel, runcommand, check
328 328 >>> @check
329 329 ... def branch(server):
330 330 ... readchannel(server)
331 331 ... runcommand(server, ['branch'])
332 332 ... os.system('hg branch foo')
333 333 ... runcommand(server, ['branch'])
334 334 ... os.system('hg branch default')
335 335 *** runcommand branch
336 336 default
337 337 marked working directory as branch foo
338 338 (branches are permanent and global, did you want a bookmark?)
339 339 *** runcommand branch
340 340 foo
341 341 marked working directory as branch default
342 342 (branches are permanent and global, did you want a bookmark?)
343 343
344 344 $ touch .hgignore
345 345 >>> import os
346 346 >>> from hgclient import readchannel, runcommand, check
347 347 >>> @check
348 348 ... def hgignore(server):
349 349 ... readchannel(server)
350 350 ... runcommand(server, ['commit', '-Am.'])
351 351 ... f = open('ignored-file', 'ab')
352 352 ... f.write('')
353 353 ... f.close()
354 354 ... f = open('.hgignore', 'ab')
355 355 ... f.write('ignored-file')
356 356 ... f.close()
357 357 ... runcommand(server, ['status', '-i', '-u'])
358 358 *** runcommand commit -Am.
359 359 adding .hgignore
360 360 *** runcommand status -i -u
361 361 I ignored-file
362 362
363 363 >>> import os
364 364 >>> from hgclient import readchannel, sep, runcommand, check
365 365 >>> @check
366 366 ... def phasecacheafterstrip(server):
367 367 ... readchannel(server)
368 368 ...
369 369 ... # create new head, 5:731265503d86
370 370 ... runcommand(server, ['update', '-C', '0'])
371 371 ... f = open('a', 'ab')
372 372 ... f.write('a\n')
373 373 ... f.close()
374 374 ... runcommand(server, ['commit', '-Am.', 'a'])
375 375 ... runcommand(server, ['log', '-Gq'])
376 376 ...
377 377 ... # make it public; draft marker moves to 4:7966c8e3734d
378 378 ... runcommand(server, ['phase', '-p', '.'])
379 379 ... # load _phasecache.phaseroots
380 380 ... runcommand(server, ['phase', '.'], outfilter=sep)
381 381 ...
382 382 ... # strip 1::4 outside server
383 383 ... os.system('hg -q --config extensions.mq= strip 1')
384 384 ...
385 385 ... # shouldn't raise "7966c8e3734d: no node!"
386 386 ... runcommand(server, ['branches'])
387 387 *** runcommand update -C 0
388 388 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
389 389 (leaving bookmark bm3)
390 390 *** runcommand commit -Am. a
391 391 created new head
392 392 *** runcommand log -Gq
393 393 @ 5:731265503d86
394 394 |
395 395 | o 4:7966c8e3734d
396 396 | |
397 397 | o 3:b9b85890c400
398 398 | |
399 399 | o 2:aef17e88f5f0
400 400 | |
401 401 | o 1:d3a0a68be6de
402 402 |/
403 403 o 0:eff892de26ec
404 404
405 405 *** runcommand phase -p .
406 406 *** runcommand phase .
407 407 5: public
408 408 *** runcommand branches
409 409 default 1:731265503d86
410 410
411 411 $ cat <<EOF > obs.py
412 412 > import mercurial.obsolete
413 413 > mercurial.obsolete._enabled = True
414 414 > EOF
415 415 $ cat <<EOF >> .hg/hgrc
416 416 > [extensions]
417 417 > obs = obs.py
418 418 > EOF
419 419
420 420 >>> import os
421 421 >>> from hgclient import readchannel, runcommand, check
422 422 >>> @check
423 423 ... def obsolete(server):
424 424 ... readchannel(server)
425 425 ...
426 426 ... runcommand(server, ['up', 'null'])
427 427 ... runcommand(server, ['phase', '-df', 'tip'])
428 428 ... cmd = 'hg debugobsolete `hg log -r tip --template {node}`'
429 429 ... if os.name == 'nt':
430 430 ... cmd = 'sh -c "%s"' % cmd # run in sh, not cmd.exe
431 431 ... os.system(cmd)
432 432 ... runcommand(server, ['log', '--hidden'])
433 433 ... runcommand(server, ['log'])
434 434 *** runcommand up null
435 435 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
436 436 *** runcommand phase -df tip
437 437 *** runcommand log --hidden
438 438 changeset: 1:731265503d86
439 439 tag: tip
440 440 user: test
441 441 date: Thu Jan 01 00:00:00 1970 +0000
442 442 summary: .
443 443
444 444 changeset: 0:eff892de26ec
445 445 bookmark: bm1
446 446 bookmark: bm2
447 447 bookmark: bm3
448 448 user: test
449 449 date: Thu Jan 01 00:00:00 1970 +0000
450 450 summary: 1
451 451
452 452 *** runcommand log
453 453 changeset: 0:eff892de26ec
454 454 bookmark: bm1
455 455 bookmark: bm2
456 456 bookmark: bm3
457 457 tag: tip
458 458 user: test
459 459 date: Thu Jan 01 00:00:00 1970 +0000
460 460 summary: 1
461 461
462 462
463 463 $ cat <<EOF >> .hg/hgrc
464 464 > [extensions]
465 465 > mq =
466 466 > EOF
467 467
468 468 >>> import os
469 469 >>> from hgclient import readchannel, runcommand, check
470 470 >>> @check
471 471 ... def mqoutsidechanges(server):
472 472 ... readchannel(server)
473 473 ...
474 474 ... # load repo.mq
475 475 ... runcommand(server, ['qapplied'])
476 476 ... os.system('hg qnew 0.diff')
477 477 ... # repo.mq should be invalidated
478 478 ... runcommand(server, ['qapplied'])
479 479 ...
480 480 ... runcommand(server, ['qpop', '--all'])
481 481 ... os.system('hg qqueue --create foo')
482 482 ... # repo.mq should be recreated to point to new queue
483 483 ... runcommand(server, ['qqueue', '--active'])
484 484 *** runcommand qapplied
485 485 *** runcommand qapplied
486 486 0.diff
487 487 *** runcommand qpop --all
488 488 popping 0.diff
489 489 patch queue now empty
490 490 *** runcommand qqueue --active
491 491 foo
492 492
493 493 $ cat <<EOF > dbgui.py
494 494 > from mercurial import cmdutil, commands
495 495 > cmdtable = {}
496 496 > command = cmdutil.command(cmdtable)
497 497 > @command("debuggetpass", norepo=True)
498 498 > def debuggetpass(ui):
499 499 > ui.write("%s\\n" % ui.getpass())
500 > @command("debugprompt", norepo=True)
501 > def debugprompt(ui):
502 > ui.write("%s\\n" % ui.prompt("prompt:"))
500 503 > EOF
501 504 $ cat <<EOF >> .hg/hgrc
502 505 > [extensions]
503 506 > dbgui = dbgui.py
504 507 > EOF
505 508
506 509 >>> import cStringIO
507 510 >>> from hgclient import readchannel, runcommand, check
508 511 >>> @check
509 512 ... def getpass(server):
510 513 ... readchannel(server)
511 514 ... runcommand(server, ['debuggetpass', '--config',
512 515 ... 'ui.interactive=True'],
513 516 ... input=cStringIO.StringIO('1234\n'))
517 ... runcommand(server, ['debugprompt', '--config',
518 ... 'ui.interactive=True'],
519 ... input=cStringIO.StringIO('5678\n'))
514 520 *** runcommand debuggetpass --config ui.interactive=True
515 521 password: 1234
522 *** runcommand debugprompt --config ui.interactive=True
523 prompt: 5678
516 524
517 525
518 526 start without repository:
519 527
520 528 $ cd ..
521 529
522 530 >>> from hgclient import readchannel, runcommand, check
523 531 >>> @check
524 532 ... def hellomessage(server):
525 533 ... ch, data = readchannel(server)
526 534 ... print '%c, %r' % (ch, data)
527 535 ... # run an arbitrary command to make sure the next thing the server
528 536 ... # sends isn't part of the hello message
529 537 ... runcommand(server, ['id'])
530 538 o, 'capabilities: getencoding runcommand\nencoding: *' (glob)
531 539 *** runcommand id
532 540 abort: there is no Mercurial repository here (.hg not found)
533 541 [255]
534 542
535 543 >>> from hgclient import readchannel, runcommand, check
536 544 >>> @check
537 545 ... def startwithoutrepo(server):
538 546 ... readchannel(server)
539 547 ... runcommand(server, ['init', 'repo2'])
540 548 ... runcommand(server, ['id', '-R', 'repo2'])
541 549 *** runcommand init repo2
542 550 *** runcommand id -R repo2
543 551 000000000000 tip
General Comments 0
You need to be logged in to leave comments. Login now