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