##// END OF EJS Templates
config: give a useful hint of source for the most common command line settings...
Mads Kiilerich -
r20788:f144928d default
parent child Browse files
Show More
@@ -1,897 +1,897 b''
1 # dispatch.py - command dispatching for mercurial
1 # dispatch.py - command dispatching 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 os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
10 import util, commands, hg, fancyopts, extensions, hook, error
10 import util, commands, hg, fancyopts, extensions, hook, error
11 import cmdutil, encoding
11 import cmdutil, encoding
12 import ui as uimod
12 import ui as uimod
13
13
14 class request(object):
14 class request(object):
15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
16 ferr=None):
16 ferr=None):
17 self.args = args
17 self.args = args
18 self.ui = ui
18 self.ui = ui
19 self.repo = repo
19 self.repo = repo
20
20
21 # input/output/error streams
21 # input/output/error streams
22 self.fin = fin
22 self.fin = fin
23 self.fout = fout
23 self.fout = fout
24 self.ferr = ferr
24 self.ferr = ferr
25
25
26 def run():
26 def run():
27 "run the command in sys.argv"
27 "run the command in sys.argv"
28 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
28 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
29
29
30 def dispatch(req):
30 def dispatch(req):
31 "run the command specified in req.args"
31 "run the command specified in req.args"
32 if req.ferr:
32 if req.ferr:
33 ferr = req.ferr
33 ferr = req.ferr
34 elif req.ui:
34 elif req.ui:
35 ferr = req.ui.ferr
35 ferr = req.ui.ferr
36 else:
36 else:
37 ferr = sys.stderr
37 ferr = sys.stderr
38
38
39 try:
39 try:
40 if not req.ui:
40 if not req.ui:
41 req.ui = uimod.ui()
41 req.ui = uimod.ui()
42 if '--traceback' in req.args:
42 if '--traceback' in req.args:
43 req.ui.setconfig('ui', 'traceback', 'on')
43 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
44
44
45 # set ui streams from the request
45 # set ui streams from the request
46 if req.fin:
46 if req.fin:
47 req.ui.fin = req.fin
47 req.ui.fin = req.fin
48 if req.fout:
48 if req.fout:
49 req.ui.fout = req.fout
49 req.ui.fout = req.fout
50 if req.ferr:
50 if req.ferr:
51 req.ui.ferr = req.ferr
51 req.ui.ferr = req.ferr
52 except util.Abort, inst:
52 except util.Abort, inst:
53 ferr.write(_("abort: %s\n") % inst)
53 ferr.write(_("abort: %s\n") % inst)
54 if inst.hint:
54 if inst.hint:
55 ferr.write(_("(%s)\n") % inst.hint)
55 ferr.write(_("(%s)\n") % inst.hint)
56 return -1
56 return -1
57 except error.ParseError, inst:
57 except error.ParseError, inst:
58 if len(inst.args) > 1:
58 if len(inst.args) > 1:
59 ferr.write(_("hg: parse error at %s: %s\n") %
59 ferr.write(_("hg: parse error at %s: %s\n") %
60 (inst.args[1], inst.args[0]))
60 (inst.args[1], inst.args[0]))
61 else:
61 else:
62 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
62 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
63 return -1
63 return -1
64
64
65 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
65 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
66 starttime = time.time()
66 starttime = time.time()
67 ret = None
67 ret = None
68 try:
68 try:
69 ret = _runcatch(req)
69 ret = _runcatch(req)
70 return ret
70 return ret
71 finally:
71 finally:
72 duration = time.time() - starttime
72 duration = time.time() - starttime
73 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
73 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
74 msg, ret or 0, duration)
74 msg, ret or 0, duration)
75
75
76 def _runcatch(req):
76 def _runcatch(req):
77 def catchterm(*args):
77 def catchterm(*args):
78 raise error.SignalInterrupt
78 raise error.SignalInterrupt
79
79
80 ui = req.ui
80 ui = req.ui
81 try:
81 try:
82 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
82 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
83 num = getattr(signal, name, None)
83 num = getattr(signal, name, None)
84 if num:
84 if num:
85 signal.signal(num, catchterm)
85 signal.signal(num, catchterm)
86 except ValueError:
86 except ValueError:
87 pass # happens if called in a thread
87 pass # happens if called in a thread
88
88
89 try:
89 try:
90 try:
90 try:
91 debugger = 'pdb'
91 debugger = 'pdb'
92 debugtrace = {
92 debugtrace = {
93 'pdb' : pdb.set_trace
93 'pdb' : pdb.set_trace
94 }
94 }
95 debugmortem = {
95 debugmortem = {
96 'pdb' : pdb.post_mortem
96 'pdb' : pdb.post_mortem
97 }
97 }
98
98
99 # read --config before doing anything else
99 # read --config before doing anything else
100 # (e.g. to change trust settings for reading .hg/hgrc)
100 # (e.g. to change trust settings for reading .hg/hgrc)
101 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
101 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
102
102
103 if req.repo:
103 if req.repo:
104 # copy configs that were passed on the cmdline (--config) to
104 # copy configs that were passed on the cmdline (--config) to
105 # the repo ui
105 # the repo ui
106 for cfg in cfgs:
106 for cfg in cfgs:
107 req.repo.ui.setconfig(*cfg)
107 req.repo.ui.setconfig(*cfg, source='--config')
108
108
109 # if we are in HGPLAIN mode, then disable custom debugging
109 # if we are in HGPLAIN mode, then disable custom debugging
110 debugger = ui.config("ui", "debugger")
110 debugger = ui.config("ui", "debugger")
111 if not debugger or ui.plain():
111 if not debugger or ui.plain():
112 debugger = 'pdb'
112 debugger = 'pdb'
113
113
114 try:
114 try:
115 debugmod = __import__(debugger)
115 debugmod = __import__(debugger)
116 except ImportError:
116 except ImportError:
117 debugmod = pdb
117 debugmod = pdb
118
118
119 debugtrace[debugger] = debugmod.set_trace
119 debugtrace[debugger] = debugmod.set_trace
120 debugmortem[debugger] = debugmod.post_mortem
120 debugmortem[debugger] = debugmod.post_mortem
121
121
122 # enter the debugger before command execution
122 # enter the debugger before command execution
123 if '--debugger' in req.args:
123 if '--debugger' in req.args:
124 ui.warn(_("entering debugger - "
124 ui.warn(_("entering debugger - "
125 "type c to continue starting hg or h for help\n"))
125 "type c to continue starting hg or h for help\n"))
126
126
127 if (debugger != 'pdb' and
127 if (debugger != 'pdb' and
128 debugtrace[debugger] == debugtrace['pdb']):
128 debugtrace[debugger] == debugtrace['pdb']):
129 ui.warn(_("%s debugger specified "
129 ui.warn(_("%s debugger specified "
130 "but its module was not found\n") % debugger)
130 "but its module was not found\n") % debugger)
131
131
132 debugtrace[debugger]()
132 debugtrace[debugger]()
133 try:
133 try:
134 return _dispatch(req)
134 return _dispatch(req)
135 finally:
135 finally:
136 ui.flush()
136 ui.flush()
137 except: # re-raises
137 except: # re-raises
138 # enter the debugger when we hit an exception
138 # enter the debugger when we hit an exception
139 if '--debugger' in req.args:
139 if '--debugger' in req.args:
140 traceback.print_exc()
140 traceback.print_exc()
141 debugmortem[debugger](sys.exc_info()[2])
141 debugmortem[debugger](sys.exc_info()[2])
142 ui.traceback()
142 ui.traceback()
143 raise
143 raise
144
144
145 # Global exception handling, alphabetically
145 # Global exception handling, alphabetically
146 # Mercurial-specific first, followed by built-in and library exceptions
146 # Mercurial-specific first, followed by built-in and library exceptions
147 except error.AmbiguousCommand, inst:
147 except error.AmbiguousCommand, inst:
148 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
148 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
149 (inst.args[0], " ".join(inst.args[1])))
149 (inst.args[0], " ".join(inst.args[1])))
150 except error.ParseError, inst:
150 except error.ParseError, inst:
151 if len(inst.args) > 1:
151 if len(inst.args) > 1:
152 ui.warn(_("hg: parse error at %s: %s\n") %
152 ui.warn(_("hg: parse error at %s: %s\n") %
153 (inst.args[1], inst.args[0]))
153 (inst.args[1], inst.args[0]))
154 else:
154 else:
155 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
155 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
156 return -1
156 return -1
157 except error.LockHeld, inst:
157 except error.LockHeld, inst:
158 if inst.errno == errno.ETIMEDOUT:
158 if inst.errno == errno.ETIMEDOUT:
159 reason = _('timed out waiting for lock held by %s') % inst.locker
159 reason = _('timed out waiting for lock held by %s') % inst.locker
160 else:
160 else:
161 reason = _('lock held by %s') % inst.locker
161 reason = _('lock held by %s') % inst.locker
162 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
162 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
163 except error.LockUnavailable, inst:
163 except error.LockUnavailable, inst:
164 ui.warn(_("abort: could not lock %s: %s\n") %
164 ui.warn(_("abort: could not lock %s: %s\n") %
165 (inst.desc or inst.filename, inst.strerror))
165 (inst.desc or inst.filename, inst.strerror))
166 except error.CommandError, inst:
166 except error.CommandError, inst:
167 if inst.args[0]:
167 if inst.args[0]:
168 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
168 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
169 commands.help_(ui, inst.args[0], full=False, command=True)
169 commands.help_(ui, inst.args[0], full=False, command=True)
170 else:
170 else:
171 ui.warn(_("hg: %s\n") % inst.args[1])
171 ui.warn(_("hg: %s\n") % inst.args[1])
172 commands.help_(ui, 'shortlist')
172 commands.help_(ui, 'shortlist')
173 except error.OutOfBandError, inst:
173 except error.OutOfBandError, inst:
174 ui.warn(_("abort: remote error:\n"))
174 ui.warn(_("abort: remote error:\n"))
175 ui.warn(''.join(inst.args))
175 ui.warn(''.join(inst.args))
176 except error.RepoError, inst:
176 except error.RepoError, inst:
177 ui.warn(_("abort: %s!\n") % inst)
177 ui.warn(_("abort: %s!\n") % inst)
178 if inst.hint:
178 if inst.hint:
179 ui.warn(_("(%s)\n") % inst.hint)
179 ui.warn(_("(%s)\n") % inst.hint)
180 except error.ResponseError, inst:
180 except error.ResponseError, inst:
181 ui.warn(_("abort: %s") % inst.args[0])
181 ui.warn(_("abort: %s") % inst.args[0])
182 if not isinstance(inst.args[1], basestring):
182 if not isinstance(inst.args[1], basestring):
183 ui.warn(" %r\n" % (inst.args[1],))
183 ui.warn(" %r\n" % (inst.args[1],))
184 elif not inst.args[1]:
184 elif not inst.args[1]:
185 ui.warn(_(" empty string\n"))
185 ui.warn(_(" empty string\n"))
186 else:
186 else:
187 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
187 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
188 except error.RevlogError, inst:
188 except error.RevlogError, inst:
189 ui.warn(_("abort: %s!\n") % inst)
189 ui.warn(_("abort: %s!\n") % inst)
190 except error.SignalInterrupt:
190 except error.SignalInterrupt:
191 ui.warn(_("killed!\n"))
191 ui.warn(_("killed!\n"))
192 except error.UnknownCommand, inst:
192 except error.UnknownCommand, inst:
193 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
193 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
194 try:
194 try:
195 # check if the command is in a disabled extension
195 # check if the command is in a disabled extension
196 # (but don't check for extensions themselves)
196 # (but don't check for extensions themselves)
197 commands.help_(ui, inst.args[0], unknowncmd=True)
197 commands.help_(ui, inst.args[0], unknowncmd=True)
198 except error.UnknownCommand:
198 except error.UnknownCommand:
199 commands.help_(ui, 'shortlist')
199 commands.help_(ui, 'shortlist')
200 except error.InterventionRequired, inst:
200 except error.InterventionRequired, inst:
201 ui.warn("%s\n" % inst)
201 ui.warn("%s\n" % inst)
202 return 1
202 return 1
203 except util.Abort, inst:
203 except util.Abort, inst:
204 ui.warn(_("abort: %s\n") % inst)
204 ui.warn(_("abort: %s\n") % inst)
205 if inst.hint:
205 if inst.hint:
206 ui.warn(_("(%s)\n") % inst.hint)
206 ui.warn(_("(%s)\n") % inst.hint)
207 except ImportError, inst:
207 except ImportError, inst:
208 ui.warn(_("abort: %s!\n") % inst)
208 ui.warn(_("abort: %s!\n") % inst)
209 m = str(inst).split()[-1]
209 m = str(inst).split()[-1]
210 if m in "mpatch bdiff".split():
210 if m in "mpatch bdiff".split():
211 ui.warn(_("(did you forget to compile extensions?)\n"))
211 ui.warn(_("(did you forget to compile extensions?)\n"))
212 elif m in "zlib".split():
212 elif m in "zlib".split():
213 ui.warn(_("(is your Python install correct?)\n"))
213 ui.warn(_("(is your Python install correct?)\n"))
214 except IOError, inst:
214 except IOError, inst:
215 if util.safehasattr(inst, "code"):
215 if util.safehasattr(inst, "code"):
216 ui.warn(_("abort: %s\n") % inst)
216 ui.warn(_("abort: %s\n") % inst)
217 elif util.safehasattr(inst, "reason"):
217 elif util.safehasattr(inst, "reason"):
218 try: # usually it is in the form (errno, strerror)
218 try: # usually it is in the form (errno, strerror)
219 reason = inst.reason.args[1]
219 reason = inst.reason.args[1]
220 except (AttributeError, IndexError):
220 except (AttributeError, IndexError):
221 # it might be anything, for example a string
221 # it might be anything, for example a string
222 reason = inst.reason
222 reason = inst.reason
223 ui.warn(_("abort: error: %s\n") % reason)
223 ui.warn(_("abort: error: %s\n") % reason)
224 elif util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
224 elif util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
225 if ui.debugflag:
225 if ui.debugflag:
226 ui.warn(_("broken pipe\n"))
226 ui.warn(_("broken pipe\n"))
227 elif getattr(inst, "strerror", None):
227 elif getattr(inst, "strerror", None):
228 if getattr(inst, "filename", None):
228 if getattr(inst, "filename", None):
229 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
229 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
230 else:
230 else:
231 ui.warn(_("abort: %s\n") % inst.strerror)
231 ui.warn(_("abort: %s\n") % inst.strerror)
232 else:
232 else:
233 raise
233 raise
234 except OSError, inst:
234 except OSError, inst:
235 if getattr(inst, "filename", None) is not None:
235 if getattr(inst, "filename", None) is not None:
236 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
236 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
237 else:
237 else:
238 ui.warn(_("abort: %s\n") % inst.strerror)
238 ui.warn(_("abort: %s\n") % inst.strerror)
239 except KeyboardInterrupt:
239 except KeyboardInterrupt:
240 try:
240 try:
241 ui.warn(_("interrupted!\n"))
241 ui.warn(_("interrupted!\n"))
242 except IOError, inst:
242 except IOError, inst:
243 if inst.errno == errno.EPIPE:
243 if inst.errno == errno.EPIPE:
244 if ui.debugflag:
244 if ui.debugflag:
245 ui.warn(_("\nbroken pipe\n"))
245 ui.warn(_("\nbroken pipe\n"))
246 else:
246 else:
247 raise
247 raise
248 except MemoryError:
248 except MemoryError:
249 ui.warn(_("abort: out of memory\n"))
249 ui.warn(_("abort: out of memory\n"))
250 except SystemExit, inst:
250 except SystemExit, inst:
251 # Commands shouldn't sys.exit directly, but give a return code.
251 # Commands shouldn't sys.exit directly, but give a return code.
252 # Just in case catch this and and pass exit code to caller.
252 # Just in case catch this and and pass exit code to caller.
253 return inst.code
253 return inst.code
254 except socket.error, inst:
254 except socket.error, inst:
255 ui.warn(_("abort: %s\n") % inst.args[-1])
255 ui.warn(_("abort: %s\n") % inst.args[-1])
256 except: # re-raises
256 except: # re-raises
257 myver = util.version()
257 myver = util.version()
258 # For compatibility checking, we discard the portion of the hg
258 # For compatibility checking, we discard the portion of the hg
259 # version after the + on the assumption that if a "normal
259 # version after the + on the assumption that if a "normal
260 # user" is running a build with a + in it the packager
260 # user" is running a build with a + in it the packager
261 # probably built from fairly close to a tag and anyone with a
261 # probably built from fairly close to a tag and anyone with a
262 # 'make local' copy of hg (where the version number can be out
262 # 'make local' copy of hg (where the version number can be out
263 # of date) will be clueful enough to notice the implausible
263 # of date) will be clueful enough to notice the implausible
264 # version number and try updating.
264 # version number and try updating.
265 compare = myver.split('+')[0]
265 compare = myver.split('+')[0]
266 ct = tuplever(compare)
266 ct = tuplever(compare)
267 worst = None, ct, ''
267 worst = None, ct, ''
268 for name, mod in extensions.extensions():
268 for name, mod in extensions.extensions():
269 testedwith = getattr(mod, 'testedwith', '')
269 testedwith = getattr(mod, 'testedwith', '')
270 report = getattr(mod, 'buglink', _('the extension author.'))
270 report = getattr(mod, 'buglink', _('the extension author.'))
271 if not testedwith.strip():
271 if not testedwith.strip():
272 # We found an untested extension. It's likely the culprit.
272 # We found an untested extension. It's likely the culprit.
273 worst = name, 'unknown', report
273 worst = name, 'unknown', report
274 break
274 break
275 if compare not in testedwith.split() and testedwith != 'internal':
275 if compare not in testedwith.split() and testedwith != 'internal':
276 tested = [tuplever(v) for v in testedwith.split()]
276 tested = [tuplever(v) for v in testedwith.split()]
277 lower = [t for t in tested if t < ct]
277 lower = [t for t in tested if t < ct]
278 nearest = max(lower or tested)
278 nearest = max(lower or tested)
279 if worst[0] is None or nearest < worst[1]:
279 if worst[0] is None or nearest < worst[1]:
280 worst = name, nearest, report
280 worst = name, nearest, report
281 if worst[0] is not None:
281 if worst[0] is not None:
282 name, testedwith, report = worst
282 name, testedwith, report = worst
283 if not isinstance(testedwith, str):
283 if not isinstance(testedwith, str):
284 testedwith = '.'.join([str(c) for c in testedwith])
284 testedwith = '.'.join([str(c) for c in testedwith])
285 warning = (_('** Unknown exception encountered with '
285 warning = (_('** Unknown exception encountered with '
286 'possibly-broken third-party extension %s\n'
286 'possibly-broken third-party extension %s\n'
287 '** which supports versions %s of Mercurial.\n'
287 '** which supports versions %s of Mercurial.\n'
288 '** Please disable %s and try your action again.\n'
288 '** Please disable %s and try your action again.\n'
289 '** If that fixes the bug please report it to %s\n')
289 '** If that fixes the bug please report it to %s\n')
290 % (name, testedwith, name, report))
290 % (name, testedwith, name, report))
291 else:
291 else:
292 warning = (_("** unknown exception encountered, "
292 warning = (_("** unknown exception encountered, "
293 "please report by visiting\n") +
293 "please report by visiting\n") +
294 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
294 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
295 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
295 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
296 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
296 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
297 (_("** Extensions loaded: %s\n") %
297 (_("** Extensions loaded: %s\n") %
298 ", ".join([x[0] for x in extensions.extensions()])))
298 ", ".join([x[0] for x in extensions.extensions()])))
299 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
299 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
300 ui.warn(warning)
300 ui.warn(warning)
301 raise
301 raise
302
302
303 return -1
303 return -1
304
304
305 def tuplever(v):
305 def tuplever(v):
306 try:
306 try:
307 return tuple([int(i) for i in v.split('.')])
307 return tuple([int(i) for i in v.split('.')])
308 except ValueError:
308 except ValueError:
309 return tuple()
309 return tuple()
310
310
311 def aliasargs(fn, givenargs):
311 def aliasargs(fn, givenargs):
312 args = getattr(fn, 'args', [])
312 args = getattr(fn, 'args', [])
313 if args:
313 if args:
314 cmd = ' '.join(map(util.shellquote, args))
314 cmd = ' '.join(map(util.shellquote, args))
315
315
316 nums = []
316 nums = []
317 def replacer(m):
317 def replacer(m):
318 num = int(m.group(1)) - 1
318 num = int(m.group(1)) - 1
319 nums.append(num)
319 nums.append(num)
320 if num < len(givenargs):
320 if num < len(givenargs):
321 return givenargs[num]
321 return givenargs[num]
322 raise util.Abort(_('too few arguments for command alias'))
322 raise util.Abort(_('too few arguments for command alias'))
323 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
323 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
324 givenargs = [x for i, x in enumerate(givenargs)
324 givenargs = [x for i, x in enumerate(givenargs)
325 if i not in nums]
325 if i not in nums]
326 args = shlex.split(cmd)
326 args = shlex.split(cmd)
327 return args + givenargs
327 return args + givenargs
328
328
329 class cmdalias(object):
329 class cmdalias(object):
330 def __init__(self, name, definition, cmdtable):
330 def __init__(self, name, definition, cmdtable):
331 self.name = self.cmd = name
331 self.name = self.cmd = name
332 self.cmdname = ''
332 self.cmdname = ''
333 self.definition = definition
333 self.definition = definition
334 self.args = []
334 self.args = []
335 self.opts = []
335 self.opts = []
336 self.help = ''
336 self.help = ''
337 self.norepo = True
337 self.norepo = True
338 self.optionalrepo = False
338 self.optionalrepo = False
339 self.badalias = False
339 self.badalias = False
340
340
341 try:
341 try:
342 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
342 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
343 for alias, e in cmdtable.iteritems():
343 for alias, e in cmdtable.iteritems():
344 if e is entry:
344 if e is entry:
345 self.cmd = alias
345 self.cmd = alias
346 break
346 break
347 self.shadows = True
347 self.shadows = True
348 except error.UnknownCommand:
348 except error.UnknownCommand:
349 self.shadows = False
349 self.shadows = False
350
350
351 if not self.definition:
351 if not self.definition:
352 def fn(ui, *args):
352 def fn(ui, *args):
353 ui.warn(_("no definition for alias '%s'\n") % self.name)
353 ui.warn(_("no definition for alias '%s'\n") % self.name)
354 return 1
354 return 1
355 self.fn = fn
355 self.fn = fn
356 self.badalias = True
356 self.badalias = True
357 return
357 return
358
358
359 if self.definition.startswith('!'):
359 if self.definition.startswith('!'):
360 self.shell = True
360 self.shell = True
361 def fn(ui, *args):
361 def fn(ui, *args):
362 env = {'HG_ARGS': ' '.join((self.name,) + args)}
362 env = {'HG_ARGS': ' '.join((self.name,) + args)}
363 def _checkvar(m):
363 def _checkvar(m):
364 if m.groups()[0] == '$':
364 if m.groups()[0] == '$':
365 return m.group()
365 return m.group()
366 elif int(m.groups()[0]) <= len(args):
366 elif int(m.groups()[0]) <= len(args):
367 return m.group()
367 return m.group()
368 else:
368 else:
369 ui.debug("No argument found for substitution "
369 ui.debug("No argument found for substitution "
370 "of %i variable in alias '%s' definition."
370 "of %i variable in alias '%s' definition."
371 % (int(m.groups()[0]), self.name))
371 % (int(m.groups()[0]), self.name))
372 return ''
372 return ''
373 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
373 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
374 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
374 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
375 replace['0'] = self.name
375 replace['0'] = self.name
376 replace['@'] = ' '.join(args)
376 replace['@'] = ' '.join(args)
377 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
377 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
378 return util.system(cmd, environ=env, out=ui.fout)
378 return util.system(cmd, environ=env, out=ui.fout)
379 self.fn = fn
379 self.fn = fn
380 return
380 return
381
381
382 args = shlex.split(self.definition)
382 args = shlex.split(self.definition)
383 self.cmdname = cmd = args.pop(0)
383 self.cmdname = cmd = args.pop(0)
384 args = map(util.expandpath, args)
384 args = map(util.expandpath, args)
385
385
386 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
386 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
387 if _earlygetopt([invalidarg], args):
387 if _earlygetopt([invalidarg], args):
388 def fn(ui, *args):
388 def fn(ui, *args):
389 ui.warn(_("error in definition for alias '%s': %s may only "
389 ui.warn(_("error in definition for alias '%s': %s may only "
390 "be given on the command line\n")
390 "be given on the command line\n")
391 % (self.name, invalidarg))
391 % (self.name, invalidarg))
392 return 1
392 return 1
393
393
394 self.fn = fn
394 self.fn = fn
395 self.badalias = True
395 self.badalias = True
396 return
396 return
397
397
398 try:
398 try:
399 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
399 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
400 if len(tableentry) > 2:
400 if len(tableentry) > 2:
401 self.fn, self.opts, self.help = tableentry
401 self.fn, self.opts, self.help = tableentry
402 else:
402 else:
403 self.fn, self.opts = tableentry
403 self.fn, self.opts = tableentry
404
404
405 self.args = aliasargs(self.fn, args)
405 self.args = aliasargs(self.fn, args)
406 if cmd not in commands.norepo.split(' '):
406 if cmd not in commands.norepo.split(' '):
407 self.norepo = False
407 self.norepo = False
408 if cmd in commands.optionalrepo.split(' '):
408 if cmd in commands.optionalrepo.split(' '):
409 self.optionalrepo = True
409 self.optionalrepo = True
410 if self.help.startswith("hg " + cmd):
410 if self.help.startswith("hg " + cmd):
411 # drop prefix in old-style help lines so hg shows the alias
411 # drop prefix in old-style help lines so hg shows the alias
412 self.help = self.help[4 + len(cmd):]
412 self.help = self.help[4 + len(cmd):]
413 self.__doc__ = self.fn.__doc__
413 self.__doc__ = self.fn.__doc__
414
414
415 except error.UnknownCommand:
415 except error.UnknownCommand:
416 def fn(ui, *args):
416 def fn(ui, *args):
417 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
417 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
418 % (self.name, cmd))
418 % (self.name, cmd))
419 try:
419 try:
420 # check if the command is in a disabled extension
420 # check if the command is in a disabled extension
421 commands.help_(ui, cmd, unknowncmd=True)
421 commands.help_(ui, cmd, unknowncmd=True)
422 except error.UnknownCommand:
422 except error.UnknownCommand:
423 pass
423 pass
424 return 1
424 return 1
425 self.fn = fn
425 self.fn = fn
426 self.badalias = True
426 self.badalias = True
427 except error.AmbiguousCommand:
427 except error.AmbiguousCommand:
428 def fn(ui, *args):
428 def fn(ui, *args):
429 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
429 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
430 % (self.name, cmd))
430 % (self.name, cmd))
431 return 1
431 return 1
432 self.fn = fn
432 self.fn = fn
433 self.badalias = True
433 self.badalias = True
434
434
435 def __call__(self, ui, *args, **opts):
435 def __call__(self, ui, *args, **opts):
436 if self.shadows:
436 if self.shadows:
437 ui.debug("alias '%s' shadows command '%s'\n" %
437 ui.debug("alias '%s' shadows command '%s'\n" %
438 (self.name, self.cmdname))
438 (self.name, self.cmdname))
439
439
440 if util.safehasattr(self, 'shell'):
440 if util.safehasattr(self, 'shell'):
441 return self.fn(ui, *args, **opts)
441 return self.fn(ui, *args, **opts)
442 else:
442 else:
443 try:
443 try:
444 util.checksignature(self.fn)(ui, *args, **opts)
444 util.checksignature(self.fn)(ui, *args, **opts)
445 except error.SignatureError:
445 except error.SignatureError:
446 args = ' '.join([self.cmdname] + self.args)
446 args = ' '.join([self.cmdname] + self.args)
447 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
447 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
448 raise
448 raise
449
449
450 def addaliases(ui, cmdtable):
450 def addaliases(ui, cmdtable):
451 # aliases are processed after extensions have been loaded, so they
451 # aliases are processed after extensions have been loaded, so they
452 # may use extension commands. Aliases can also use other alias definitions,
452 # may use extension commands. Aliases can also use other alias definitions,
453 # but only if they have been defined prior to the current definition.
453 # but only if they have been defined prior to the current definition.
454 for alias, definition in ui.configitems('alias'):
454 for alias, definition in ui.configitems('alias'):
455 aliasdef = cmdalias(alias, definition, cmdtable)
455 aliasdef = cmdalias(alias, definition, cmdtable)
456
456
457 try:
457 try:
458 olddef = cmdtable[aliasdef.cmd][0]
458 olddef = cmdtable[aliasdef.cmd][0]
459 if olddef.definition == aliasdef.definition:
459 if olddef.definition == aliasdef.definition:
460 continue
460 continue
461 except (KeyError, AttributeError):
461 except (KeyError, AttributeError):
462 # definition might not exist or it might not be a cmdalias
462 # definition might not exist or it might not be a cmdalias
463 pass
463 pass
464
464
465 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
465 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
466 if aliasdef.norepo:
466 if aliasdef.norepo:
467 commands.norepo += ' %s' % alias
467 commands.norepo += ' %s' % alias
468 if aliasdef.optionalrepo:
468 if aliasdef.optionalrepo:
469 commands.optionalrepo += ' %s' % alias
469 commands.optionalrepo += ' %s' % alias
470
470
471 def _parse(ui, args):
471 def _parse(ui, args):
472 options = {}
472 options = {}
473 cmdoptions = {}
473 cmdoptions = {}
474
474
475 try:
475 try:
476 args = fancyopts.fancyopts(args, commands.globalopts, options)
476 args = fancyopts.fancyopts(args, commands.globalopts, options)
477 except fancyopts.getopt.GetoptError, inst:
477 except fancyopts.getopt.GetoptError, inst:
478 raise error.CommandError(None, inst)
478 raise error.CommandError(None, inst)
479
479
480 if args:
480 if args:
481 cmd, args = args[0], args[1:]
481 cmd, args = args[0], args[1:]
482 aliases, entry = cmdutil.findcmd(cmd, commands.table,
482 aliases, entry = cmdutil.findcmd(cmd, commands.table,
483 ui.configbool("ui", "strict"))
483 ui.configbool("ui", "strict"))
484 cmd = aliases[0]
484 cmd = aliases[0]
485 args = aliasargs(entry[0], args)
485 args = aliasargs(entry[0], args)
486 defaults = ui.config("defaults", cmd)
486 defaults = ui.config("defaults", cmd)
487 if defaults:
487 if defaults:
488 args = map(util.expandpath, shlex.split(defaults)) + args
488 args = map(util.expandpath, shlex.split(defaults)) + args
489 c = list(entry[1])
489 c = list(entry[1])
490 else:
490 else:
491 cmd = None
491 cmd = None
492 c = []
492 c = []
493
493
494 # combine global options into local
494 # combine global options into local
495 for o in commands.globalopts:
495 for o in commands.globalopts:
496 c.append((o[0], o[1], options[o[1]], o[3]))
496 c.append((o[0], o[1], options[o[1]], o[3]))
497
497
498 try:
498 try:
499 args = fancyopts.fancyopts(args, c, cmdoptions, True)
499 args = fancyopts.fancyopts(args, c, cmdoptions, True)
500 except fancyopts.getopt.GetoptError, inst:
500 except fancyopts.getopt.GetoptError, inst:
501 raise error.CommandError(cmd, inst)
501 raise error.CommandError(cmd, inst)
502
502
503 # separate global options back out
503 # separate global options back out
504 for o in commands.globalopts:
504 for o in commands.globalopts:
505 n = o[1]
505 n = o[1]
506 options[n] = cmdoptions[n]
506 options[n] = cmdoptions[n]
507 del cmdoptions[n]
507 del cmdoptions[n]
508
508
509 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
509 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
510
510
511 def _parseconfig(ui, config):
511 def _parseconfig(ui, config):
512 """parse the --config options from the command line"""
512 """parse the --config options from the command line"""
513 configs = []
513 configs = []
514
514
515 for cfg in config:
515 for cfg in config:
516 try:
516 try:
517 name, value = cfg.split('=', 1)
517 name, value = cfg.split('=', 1)
518 section, name = name.split('.', 1)
518 section, name = name.split('.', 1)
519 if not section or not name:
519 if not section or not name:
520 raise IndexError
520 raise IndexError
521 ui.setconfig(section, name, value)
521 ui.setconfig(section, name, value, '--config')
522 configs.append((section, name, value))
522 configs.append((section, name, value))
523 except (IndexError, ValueError):
523 except (IndexError, ValueError):
524 raise util.Abort(_('malformed --config option: %r '
524 raise util.Abort(_('malformed --config option: %r '
525 '(use --config section.name=value)') % cfg)
525 '(use --config section.name=value)') % cfg)
526
526
527 return configs
527 return configs
528
528
529 def _earlygetopt(aliases, args):
529 def _earlygetopt(aliases, args):
530 """Return list of values for an option (or aliases).
530 """Return list of values for an option (or aliases).
531
531
532 The values are listed in the order they appear in args.
532 The values are listed in the order they appear in args.
533 The options and values are removed from args.
533 The options and values are removed from args.
534
534
535 >>> args = ['x', '--cwd', 'foo', 'y']
535 >>> args = ['x', '--cwd', 'foo', 'y']
536 >>> _earlygetopt(['--cwd'], args), args
536 >>> _earlygetopt(['--cwd'], args), args
537 (['foo'], ['x', 'y'])
537 (['foo'], ['x', 'y'])
538
538
539 >>> args = ['x', '--cwd=bar', 'y']
539 >>> args = ['x', '--cwd=bar', 'y']
540 >>> _earlygetopt(['--cwd'], args), args
540 >>> _earlygetopt(['--cwd'], args), args
541 (['bar'], ['x', 'y'])
541 (['bar'], ['x', 'y'])
542
542
543 >>> args = ['x', '-R', 'foo', 'y']
543 >>> args = ['x', '-R', 'foo', 'y']
544 >>> _earlygetopt(['-R'], args), args
544 >>> _earlygetopt(['-R'], args), args
545 (['foo'], ['x', 'y'])
545 (['foo'], ['x', 'y'])
546
546
547 >>> args = ['x', '-Rbar', 'y']
547 >>> args = ['x', '-Rbar', 'y']
548 >>> _earlygetopt(['-R'], args), args
548 >>> _earlygetopt(['-R'], args), args
549 (['bar'], ['x', 'y'])
549 (['bar'], ['x', 'y'])
550 """
550 """
551 try:
551 try:
552 argcount = args.index("--")
552 argcount = args.index("--")
553 except ValueError:
553 except ValueError:
554 argcount = len(args)
554 argcount = len(args)
555 shortopts = [opt for opt in aliases if len(opt) == 2]
555 shortopts = [opt for opt in aliases if len(opt) == 2]
556 values = []
556 values = []
557 pos = 0
557 pos = 0
558 while pos < argcount:
558 while pos < argcount:
559 fullarg = arg = args[pos]
559 fullarg = arg = args[pos]
560 equals = arg.find('=')
560 equals = arg.find('=')
561 if equals > -1:
561 if equals > -1:
562 arg = arg[:equals]
562 arg = arg[:equals]
563 if arg in aliases:
563 if arg in aliases:
564 del args[pos]
564 del args[pos]
565 if equals > -1:
565 if equals > -1:
566 values.append(fullarg[equals + 1:])
566 values.append(fullarg[equals + 1:])
567 argcount -= 1
567 argcount -= 1
568 else:
568 else:
569 if pos + 1 >= argcount:
569 if pos + 1 >= argcount:
570 # ignore and let getopt report an error if there is no value
570 # ignore and let getopt report an error if there is no value
571 break
571 break
572 values.append(args.pop(pos))
572 values.append(args.pop(pos))
573 argcount -= 2
573 argcount -= 2
574 elif arg[:2] in shortopts:
574 elif arg[:2] in shortopts:
575 # short option can have no following space, e.g. hg log -Rfoo
575 # short option can have no following space, e.g. hg log -Rfoo
576 values.append(args.pop(pos)[2:])
576 values.append(args.pop(pos)[2:])
577 argcount -= 1
577 argcount -= 1
578 else:
578 else:
579 pos += 1
579 pos += 1
580 return values
580 return values
581
581
582 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
582 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
583 # run pre-hook, and abort if it fails
583 # run pre-hook, and abort if it fails
584 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
584 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
585 pats=cmdpats, opts=cmdoptions)
585 pats=cmdpats, opts=cmdoptions)
586 ret = _runcommand(ui, options, cmd, d)
586 ret = _runcommand(ui, options, cmd, d)
587 # run post-hook, passing command result
587 # run post-hook, passing command result
588 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
588 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
589 result=ret, pats=cmdpats, opts=cmdoptions)
589 result=ret, pats=cmdpats, opts=cmdoptions)
590 return ret
590 return ret
591
591
592 def _getlocal(ui, rpath):
592 def _getlocal(ui, rpath):
593 """Return (path, local ui object) for the given target path.
593 """Return (path, local ui object) for the given target path.
594
594
595 Takes paths in [cwd]/.hg/hgrc into account."
595 Takes paths in [cwd]/.hg/hgrc into account."
596 """
596 """
597 try:
597 try:
598 wd = os.getcwd()
598 wd = os.getcwd()
599 except OSError, e:
599 except OSError, e:
600 raise util.Abort(_("error getting current working directory: %s") %
600 raise util.Abort(_("error getting current working directory: %s") %
601 e.strerror)
601 e.strerror)
602 path = cmdutil.findrepo(wd) or ""
602 path = cmdutil.findrepo(wd) or ""
603 if not path:
603 if not path:
604 lui = ui
604 lui = ui
605 else:
605 else:
606 lui = ui.copy()
606 lui = ui.copy()
607 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
607 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
608
608
609 if rpath and rpath[-1]:
609 if rpath and rpath[-1]:
610 path = lui.expandpath(rpath[-1])
610 path = lui.expandpath(rpath[-1])
611 lui = ui.copy()
611 lui = ui.copy()
612 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
612 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
613
613
614 return path, lui
614 return path, lui
615
615
616 def _checkshellalias(lui, ui, args):
616 def _checkshellalias(lui, ui, args):
617 options = {}
617 options = {}
618
618
619 try:
619 try:
620 args = fancyopts.fancyopts(args, commands.globalopts, options)
620 args = fancyopts.fancyopts(args, commands.globalopts, options)
621 except fancyopts.getopt.GetoptError:
621 except fancyopts.getopt.GetoptError:
622 return
622 return
623
623
624 if not args:
624 if not args:
625 return
625 return
626
626
627 norepo = commands.norepo
627 norepo = commands.norepo
628 optionalrepo = commands.optionalrepo
628 optionalrepo = commands.optionalrepo
629 def restorecommands():
629 def restorecommands():
630 commands.norepo = norepo
630 commands.norepo = norepo
631 commands.optionalrepo = optionalrepo
631 commands.optionalrepo = optionalrepo
632
632
633 cmdtable = commands.table.copy()
633 cmdtable = commands.table.copy()
634 addaliases(lui, cmdtable)
634 addaliases(lui, cmdtable)
635
635
636 cmd = args[0]
636 cmd = args[0]
637 try:
637 try:
638 aliases, entry = cmdutil.findcmd(cmd, cmdtable)
638 aliases, entry = cmdutil.findcmd(cmd, cmdtable)
639 except (error.AmbiguousCommand, error.UnknownCommand):
639 except (error.AmbiguousCommand, error.UnknownCommand):
640 restorecommands()
640 restorecommands()
641 return
641 return
642
642
643 cmd = aliases[0]
643 cmd = aliases[0]
644 fn = entry[0]
644 fn = entry[0]
645
645
646 if cmd and util.safehasattr(fn, 'shell'):
646 if cmd and util.safehasattr(fn, 'shell'):
647 d = lambda: fn(ui, *args[1:])
647 d = lambda: fn(ui, *args[1:])
648 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
648 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
649 [], {})
649 [], {})
650
650
651 restorecommands()
651 restorecommands()
652
652
653 _loaded = set()
653 _loaded = set()
654 def _dispatch(req):
654 def _dispatch(req):
655 args = req.args
655 args = req.args
656 ui = req.ui
656 ui = req.ui
657
657
658 # check for cwd
658 # check for cwd
659 cwd = _earlygetopt(['--cwd'], args)
659 cwd = _earlygetopt(['--cwd'], args)
660 if cwd:
660 if cwd:
661 os.chdir(cwd[-1])
661 os.chdir(cwd[-1])
662
662
663 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
663 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
664 path, lui = _getlocal(ui, rpath)
664 path, lui = _getlocal(ui, rpath)
665
665
666 # Now that we're operating in the right directory/repository with
666 # Now that we're operating in the right directory/repository with
667 # the right config settings, check for shell aliases
667 # the right config settings, check for shell aliases
668 shellaliasfn = _checkshellalias(lui, ui, args)
668 shellaliasfn = _checkshellalias(lui, ui, args)
669 if shellaliasfn:
669 if shellaliasfn:
670 return shellaliasfn()
670 return shellaliasfn()
671
671
672 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
672 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
673 # reposetup. Programs like TortoiseHg will call _dispatch several
673 # reposetup. Programs like TortoiseHg will call _dispatch several
674 # times so we keep track of configured extensions in _loaded.
674 # times so we keep track of configured extensions in _loaded.
675 extensions.loadall(lui)
675 extensions.loadall(lui)
676 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
676 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
677 # Propagate any changes to lui.__class__ by extensions
677 # Propagate any changes to lui.__class__ by extensions
678 ui.__class__ = lui.__class__
678 ui.__class__ = lui.__class__
679
679
680 # (uisetup and extsetup are handled in extensions.loadall)
680 # (uisetup and extsetup are handled in extensions.loadall)
681
681
682 for name, module in exts:
682 for name, module in exts:
683 cmdtable = getattr(module, 'cmdtable', {})
683 cmdtable = getattr(module, 'cmdtable', {})
684 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
684 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
685 if overrides:
685 if overrides:
686 ui.warn(_("extension '%s' overrides commands: %s\n")
686 ui.warn(_("extension '%s' overrides commands: %s\n")
687 % (name, " ".join(overrides)))
687 % (name, " ".join(overrides)))
688 commands.table.update(cmdtable)
688 commands.table.update(cmdtable)
689 _loaded.add(name)
689 _loaded.add(name)
690
690
691 # (reposetup is handled in hg.repository)
691 # (reposetup is handled in hg.repository)
692
692
693 addaliases(lui, commands.table)
693 addaliases(lui, commands.table)
694
694
695 # check for fallback encoding
695 # check for fallback encoding
696 fallback = lui.config('ui', 'fallbackencoding')
696 fallback = lui.config('ui', 'fallbackencoding')
697 if fallback:
697 if fallback:
698 encoding.fallbackencoding = fallback
698 encoding.fallbackencoding = fallback
699
699
700 fullargs = args
700 fullargs = args
701 cmd, func, args, options, cmdoptions = _parse(lui, args)
701 cmd, func, args, options, cmdoptions = _parse(lui, args)
702
702
703 if options["config"]:
703 if options["config"]:
704 raise util.Abort(_("option --config may not be abbreviated!"))
704 raise util.Abort(_("option --config may not be abbreviated!"))
705 if options["cwd"]:
705 if options["cwd"]:
706 raise util.Abort(_("option --cwd may not be abbreviated!"))
706 raise util.Abort(_("option --cwd may not be abbreviated!"))
707 if options["repository"]:
707 if options["repository"]:
708 raise util.Abort(_(
708 raise util.Abort(_(
709 "option -R has to be separated from other options (e.g. not -qR) "
709 "option -R has to be separated from other options (e.g. not -qR) "
710 "and --repository may only be abbreviated as --repo!"))
710 "and --repository may only be abbreviated as --repo!"))
711
711
712 if options["encoding"]:
712 if options["encoding"]:
713 encoding.encoding = options["encoding"]
713 encoding.encoding = options["encoding"]
714 if options["encodingmode"]:
714 if options["encodingmode"]:
715 encoding.encodingmode = options["encodingmode"]
715 encoding.encodingmode = options["encodingmode"]
716 if options["time"]:
716 if options["time"]:
717 def get_times():
717 def get_times():
718 t = os.times()
718 t = os.times()
719 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
719 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
720 t = (t[0], t[1], t[2], t[3], time.clock())
720 t = (t[0], t[1], t[2], t[3], time.clock())
721 return t
721 return t
722 s = get_times()
722 s = get_times()
723 def print_time():
723 def print_time():
724 t = get_times()
724 t = get_times()
725 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
725 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
726 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
726 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
727 atexit.register(print_time)
727 atexit.register(print_time)
728
728
729 uis = set([ui, lui])
729 uis = set([ui, lui])
730
730
731 if req.repo:
731 if req.repo:
732 uis.add(req.repo.ui)
732 uis.add(req.repo.ui)
733
733
734 if options['verbose'] or options['debug'] or options['quiet']:
734 if options['verbose'] or options['debug'] or options['quiet']:
735 for opt in ('verbose', 'debug', 'quiet'):
735 for opt in ('verbose', 'debug', 'quiet'):
736 val = str(bool(options[opt]))
736 val = str(bool(options[opt]))
737 for ui_ in uis:
737 for ui_ in uis:
738 ui_.setconfig('ui', opt, val)
738 ui_.setconfig('ui', opt, val, '--' + opt)
739
739
740 if options['traceback']:
740 if options['traceback']:
741 for ui_ in uis:
741 for ui_ in uis:
742 ui_.setconfig('ui', 'traceback', 'on')
742 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
743
743
744 if options['noninteractive']:
744 if options['noninteractive']:
745 for ui_ in uis:
745 for ui_ in uis:
746 ui_.setconfig('ui', 'interactive', 'off')
746 ui_.setconfig('ui', 'interactive', 'off', '-y')
747
747
748 if cmdoptions.get('insecure', False):
748 if cmdoptions.get('insecure', False):
749 for ui_ in uis:
749 for ui_ in uis:
750 ui_.setconfig('web', 'cacerts', '')
750 ui_.setconfig('web', 'cacerts', '', '--insecure')
751
751
752 if options['version']:
752 if options['version']:
753 return commands.version_(ui)
753 return commands.version_(ui)
754 if options['help']:
754 if options['help']:
755 return commands.help_(ui, cmd)
755 return commands.help_(ui, cmd)
756 elif not cmd:
756 elif not cmd:
757 return commands.help_(ui, 'shortlist')
757 return commands.help_(ui, 'shortlist')
758
758
759 repo = None
759 repo = None
760 cmdpats = args[:]
760 cmdpats = args[:]
761 if cmd not in commands.norepo.split():
761 if cmd not in commands.norepo.split():
762 # use the repo from the request only if we don't have -R
762 # use the repo from the request only if we don't have -R
763 if not rpath and not cwd:
763 if not rpath and not cwd:
764 repo = req.repo
764 repo = req.repo
765
765
766 if repo:
766 if repo:
767 # set the descriptors of the repo ui to those of ui
767 # set the descriptors of the repo ui to those of ui
768 repo.ui.fin = ui.fin
768 repo.ui.fin = ui.fin
769 repo.ui.fout = ui.fout
769 repo.ui.fout = ui.fout
770 repo.ui.ferr = ui.ferr
770 repo.ui.ferr = ui.ferr
771 else:
771 else:
772 try:
772 try:
773 repo = hg.repository(ui, path=path)
773 repo = hg.repository(ui, path=path)
774 if not repo.local():
774 if not repo.local():
775 raise util.Abort(_("repository '%s' is not local") % path)
775 raise util.Abort(_("repository '%s' is not local") % path)
776 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
776 repo.ui.setconfig("bundle", "mainreporoot", repo.root, 'repo')
777 except error.RequirementError:
777 except error.RequirementError:
778 raise
778 raise
779 except error.RepoError:
779 except error.RepoError:
780 if cmd not in commands.optionalrepo.split():
780 if cmd not in commands.optionalrepo.split():
781 if (cmd in commands.inferrepo.split() and
781 if (cmd in commands.inferrepo.split() and
782 args and not path): # try to infer -R from command args
782 args and not path): # try to infer -R from command args
783 repos = map(cmdutil.findrepo, args)
783 repos = map(cmdutil.findrepo, args)
784 guess = repos[0]
784 guess = repos[0]
785 if guess and repos.count(guess) == len(repos):
785 if guess and repos.count(guess) == len(repos):
786 req.args = ['--repository', guess] + fullargs
786 req.args = ['--repository', guess] + fullargs
787 return _dispatch(req)
787 return _dispatch(req)
788 if not path:
788 if not path:
789 raise error.RepoError(_("no repository found in '%s'"
789 raise error.RepoError(_("no repository found in '%s'"
790 " (.hg not found)")
790 " (.hg not found)")
791 % os.getcwd())
791 % os.getcwd())
792 raise
792 raise
793 if repo:
793 if repo:
794 ui = repo.ui
794 ui = repo.ui
795 if options['hidden']:
795 if options['hidden']:
796 repo = repo.unfiltered()
796 repo = repo.unfiltered()
797 args.insert(0, repo)
797 args.insert(0, repo)
798 elif rpath:
798 elif rpath:
799 ui.warn(_("warning: --repository ignored\n"))
799 ui.warn(_("warning: --repository ignored\n"))
800
800
801 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
801 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
802 ui.log("command", '%s\n', msg)
802 ui.log("command", '%s\n', msg)
803 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
803 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
804 try:
804 try:
805 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
805 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
806 cmdpats, cmdoptions)
806 cmdpats, cmdoptions)
807 finally:
807 finally:
808 if repo and repo != req.repo:
808 if repo and repo != req.repo:
809 repo.close()
809 repo.close()
810
810
811 def lsprofile(ui, func, fp):
811 def lsprofile(ui, func, fp):
812 format = ui.config('profiling', 'format', default='text')
812 format = ui.config('profiling', 'format', default='text')
813 field = ui.config('profiling', 'sort', default='inlinetime')
813 field = ui.config('profiling', 'sort', default='inlinetime')
814 limit = ui.configint('profiling', 'limit', default=30)
814 limit = ui.configint('profiling', 'limit', default=30)
815 climit = ui.configint('profiling', 'nested', default=5)
815 climit = ui.configint('profiling', 'nested', default=5)
816
816
817 if format not in ['text', 'kcachegrind']:
817 if format not in ['text', 'kcachegrind']:
818 ui.warn(_("unrecognized profiling format '%s'"
818 ui.warn(_("unrecognized profiling format '%s'"
819 " - Ignored\n") % format)
819 " - Ignored\n") % format)
820 format = 'text'
820 format = 'text'
821
821
822 try:
822 try:
823 from mercurial import lsprof
823 from mercurial import lsprof
824 except ImportError:
824 except ImportError:
825 raise util.Abort(_(
825 raise util.Abort(_(
826 'lsprof not available - install from '
826 'lsprof not available - install from '
827 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
827 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
828 p = lsprof.Profiler()
828 p = lsprof.Profiler()
829 p.enable(subcalls=True)
829 p.enable(subcalls=True)
830 try:
830 try:
831 return func()
831 return func()
832 finally:
832 finally:
833 p.disable()
833 p.disable()
834
834
835 if format == 'kcachegrind':
835 if format == 'kcachegrind':
836 import lsprofcalltree
836 import lsprofcalltree
837 calltree = lsprofcalltree.KCacheGrind(p)
837 calltree = lsprofcalltree.KCacheGrind(p)
838 calltree.output(fp)
838 calltree.output(fp)
839 else:
839 else:
840 # format == 'text'
840 # format == 'text'
841 stats = lsprof.Stats(p.getstats())
841 stats = lsprof.Stats(p.getstats())
842 stats.sort(field)
842 stats.sort(field)
843 stats.pprint(limit=limit, file=fp, climit=climit)
843 stats.pprint(limit=limit, file=fp, climit=climit)
844
844
845 def statprofile(ui, func, fp):
845 def statprofile(ui, func, fp):
846 try:
846 try:
847 import statprof
847 import statprof
848 except ImportError:
848 except ImportError:
849 raise util.Abort(_(
849 raise util.Abort(_(
850 'statprof not available - install using "easy_install statprof"'))
850 'statprof not available - install using "easy_install statprof"'))
851
851
852 freq = ui.configint('profiling', 'freq', default=1000)
852 freq = ui.configint('profiling', 'freq', default=1000)
853 if freq > 0:
853 if freq > 0:
854 statprof.reset(freq)
854 statprof.reset(freq)
855 else:
855 else:
856 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
856 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
857
857
858 statprof.start()
858 statprof.start()
859 try:
859 try:
860 return func()
860 return func()
861 finally:
861 finally:
862 statprof.stop()
862 statprof.stop()
863 statprof.display(fp)
863 statprof.display(fp)
864
864
865 def _runcommand(ui, options, cmd, cmdfunc):
865 def _runcommand(ui, options, cmd, cmdfunc):
866 def checkargs():
866 def checkargs():
867 try:
867 try:
868 return cmdfunc()
868 return cmdfunc()
869 except error.SignatureError:
869 except error.SignatureError:
870 raise error.CommandError(cmd, _("invalid arguments"))
870 raise error.CommandError(cmd, _("invalid arguments"))
871
871
872 if options['profile']:
872 if options['profile']:
873 profiler = os.getenv('HGPROF')
873 profiler = os.getenv('HGPROF')
874 if profiler is None:
874 if profiler is None:
875 profiler = ui.config('profiling', 'type', default='ls')
875 profiler = ui.config('profiling', 'type', default='ls')
876 if profiler not in ('ls', 'stat'):
876 if profiler not in ('ls', 'stat'):
877 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
877 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
878 profiler = 'ls'
878 profiler = 'ls'
879
879
880 output = ui.config('profiling', 'output')
880 output = ui.config('profiling', 'output')
881
881
882 if output:
882 if output:
883 path = ui.expandpath(output)
883 path = ui.expandpath(output)
884 fp = open(path, 'wb')
884 fp = open(path, 'wb')
885 else:
885 else:
886 fp = sys.stderr
886 fp = sys.stderr
887
887
888 try:
888 try:
889 if profiler == 'ls':
889 if profiler == 'ls':
890 return lsprofile(ui, checkargs, fp)
890 return lsprofile(ui, checkargs, fp)
891 else:
891 else:
892 return statprofile(ui, checkargs, fp)
892 return statprofile(ui, checkargs, fp)
893 finally:
893 finally:
894 if output:
894 if output:
895 fp.close()
895 fp.close()
896 else:
896 else:
897 return checkargs()
897 return checkargs()
@@ -1,836 +1,836 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 class ui(object):
13 class ui(object):
14 def __init__(self, src=None):
14 def __init__(self, src=None):
15 self._buffers = []
15 self._buffers = []
16 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
16 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
17 self._reportuntrusted = True
17 self._reportuntrusted = True
18 self._ocfg = config.config() # overlay
18 self._ocfg = config.config() # overlay
19 self._tcfg = config.config() # trusted
19 self._tcfg = config.config() # trusted
20 self._ucfg = config.config() # untrusted
20 self._ucfg = config.config() # untrusted
21 self._trustusers = set()
21 self._trustusers = set()
22 self._trustgroups = set()
22 self._trustgroups = set()
23 self.callhooks = True
23 self.callhooks = True
24
24
25 if src:
25 if src:
26 self.fout = src.fout
26 self.fout = src.fout
27 self.ferr = src.ferr
27 self.ferr = src.ferr
28 self.fin = src.fin
28 self.fin = src.fin
29
29
30 self._tcfg = src._tcfg.copy()
30 self._tcfg = src._tcfg.copy()
31 self._ucfg = src._ucfg.copy()
31 self._ucfg = src._ucfg.copy()
32 self._ocfg = src._ocfg.copy()
32 self._ocfg = src._ocfg.copy()
33 self._trustusers = src._trustusers.copy()
33 self._trustusers = src._trustusers.copy()
34 self._trustgroups = src._trustgroups.copy()
34 self._trustgroups = src._trustgroups.copy()
35 self.environ = src.environ
35 self.environ = src.environ
36 self.callhooks = src.callhooks
36 self.callhooks = src.callhooks
37 self.fixconfig()
37 self.fixconfig()
38 else:
38 else:
39 self.fout = sys.stdout
39 self.fout = sys.stdout
40 self.ferr = sys.stderr
40 self.ferr = sys.stderr
41 self.fin = sys.stdin
41 self.fin = sys.stdin
42
42
43 # shared read-only environment
43 # shared read-only environment
44 self.environ = os.environ
44 self.environ = os.environ
45 # we always trust global config files
45 # we always trust global config files
46 for f in scmutil.rcpath():
46 for f in scmutil.rcpath():
47 self.readconfig(f, trust=True)
47 self.readconfig(f, trust=True)
48
48
49 def copy(self):
49 def copy(self):
50 return self.__class__(self)
50 return self.__class__(self)
51
51
52 def formatter(self, topic, opts):
52 def formatter(self, topic, opts):
53 return formatter.formatter(self, topic, opts)
53 return formatter.formatter(self, topic, opts)
54
54
55 def _trusted(self, fp, f):
55 def _trusted(self, fp, f):
56 st = util.fstat(fp)
56 st = util.fstat(fp)
57 if util.isowner(st):
57 if util.isowner(st):
58 return True
58 return True
59
59
60 tusers, tgroups = self._trustusers, self._trustgroups
60 tusers, tgroups = self._trustusers, self._trustgroups
61 if '*' in tusers or '*' in tgroups:
61 if '*' in tusers or '*' in tgroups:
62 return True
62 return True
63
63
64 user = util.username(st.st_uid)
64 user = util.username(st.st_uid)
65 group = util.groupname(st.st_gid)
65 group = util.groupname(st.st_gid)
66 if user in tusers or group in tgroups or user == util.username():
66 if user in tusers or group in tgroups or user == util.username():
67 return True
67 return True
68
68
69 if self._reportuntrusted:
69 if self._reportuntrusted:
70 self.warn(_('not trusting file %s from untrusted '
70 self.warn(_('not trusting file %s from untrusted '
71 'user %s, group %s\n') % (f, user, group))
71 'user %s, group %s\n') % (f, user, group))
72 return False
72 return False
73
73
74 def readconfig(self, filename, root=None, trust=False,
74 def readconfig(self, filename, root=None, trust=False,
75 sections=None, remap=None):
75 sections=None, remap=None):
76 try:
76 try:
77 fp = open(filename)
77 fp = open(filename)
78 except IOError:
78 except IOError:
79 if not sections: # ignore unless we were looking for something
79 if not sections: # ignore unless we were looking for something
80 return
80 return
81 raise
81 raise
82
82
83 cfg = config.config()
83 cfg = config.config()
84 trusted = sections or trust or self._trusted(fp, filename)
84 trusted = sections or trust or self._trusted(fp, filename)
85
85
86 try:
86 try:
87 cfg.read(filename, fp, sections=sections, remap=remap)
87 cfg.read(filename, fp, sections=sections, remap=remap)
88 fp.close()
88 fp.close()
89 except error.ConfigError, inst:
89 except error.ConfigError, inst:
90 if trusted:
90 if trusted:
91 raise
91 raise
92 self.warn(_("ignored: %s\n") % str(inst))
92 self.warn(_("ignored: %s\n") % str(inst))
93
93
94 if self.plain():
94 if self.plain():
95 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
95 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
96 'logtemplate', 'style',
96 'logtemplate', 'style',
97 'traceback', 'verbose'):
97 'traceback', 'verbose'):
98 if k in cfg['ui']:
98 if k in cfg['ui']:
99 del cfg['ui'][k]
99 del cfg['ui'][k]
100 for k, v in cfg.items('defaults'):
100 for k, v in cfg.items('defaults'):
101 del cfg['defaults'][k]
101 del cfg['defaults'][k]
102 # Don't remove aliases from the configuration if in the exceptionlist
102 # Don't remove aliases from the configuration if in the exceptionlist
103 if self.plain('alias'):
103 if self.plain('alias'):
104 for k, v in cfg.items('alias'):
104 for k, v in cfg.items('alias'):
105 del cfg['alias'][k]
105 del cfg['alias'][k]
106
106
107 if trusted:
107 if trusted:
108 self._tcfg.update(cfg)
108 self._tcfg.update(cfg)
109 self._tcfg.update(self._ocfg)
109 self._tcfg.update(self._ocfg)
110 self._ucfg.update(cfg)
110 self._ucfg.update(cfg)
111 self._ucfg.update(self._ocfg)
111 self._ucfg.update(self._ocfg)
112
112
113 if root is None:
113 if root is None:
114 root = os.path.expanduser('~')
114 root = os.path.expanduser('~')
115 self.fixconfig(root=root)
115 self.fixconfig(root=root)
116
116
117 def fixconfig(self, root=None, section=None):
117 def fixconfig(self, root=None, section=None):
118 if section in (None, 'paths'):
118 if section in (None, 'paths'):
119 # expand vars and ~
119 # expand vars and ~
120 # translate paths relative to root (or home) into absolute paths
120 # translate paths relative to root (or home) into absolute paths
121 root = root or os.getcwd()
121 root = root or os.getcwd()
122 for c in self._tcfg, self._ucfg, self._ocfg:
122 for c in self._tcfg, self._ucfg, self._ocfg:
123 for n, p in c.items('paths'):
123 for n, p in c.items('paths'):
124 if not p:
124 if not p:
125 continue
125 continue
126 if '%%' in p:
126 if '%%' in p:
127 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
127 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
128 % (n, p, self.configsource('paths', n)))
128 % (n, p, self.configsource('paths', n)))
129 p = p.replace('%%', '%')
129 p = p.replace('%%', '%')
130 p = util.expandpath(p)
130 p = util.expandpath(p)
131 if not util.hasscheme(p) and not os.path.isabs(p):
131 if not util.hasscheme(p) and not os.path.isabs(p):
132 p = os.path.normpath(os.path.join(root, p))
132 p = os.path.normpath(os.path.join(root, p))
133 c.set("paths", n, p)
133 c.set("paths", n, p)
134
134
135 if section in (None, 'ui'):
135 if section in (None, 'ui'):
136 # update ui options
136 # update ui options
137 self.debugflag = self.configbool('ui', 'debug')
137 self.debugflag = self.configbool('ui', 'debug')
138 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
138 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
139 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
139 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
140 if self.verbose and self.quiet:
140 if self.verbose and self.quiet:
141 self.quiet = self.verbose = False
141 self.quiet = self.verbose = False
142 self._reportuntrusted = self.debugflag or self.configbool("ui",
142 self._reportuntrusted = self.debugflag or self.configbool("ui",
143 "report_untrusted", True)
143 "report_untrusted", True)
144 self.tracebackflag = self.configbool('ui', 'traceback', False)
144 self.tracebackflag = self.configbool('ui', 'traceback', False)
145
145
146 if section in (None, 'trusted'):
146 if section in (None, 'trusted'):
147 # update trust information
147 # update trust information
148 self._trustusers.update(self.configlist('trusted', 'users'))
148 self._trustusers.update(self.configlist('trusted', 'users'))
149 self._trustgroups.update(self.configlist('trusted', 'groups'))
149 self._trustgroups.update(self.configlist('trusted', 'groups'))
150
150
151 def backupconfig(self, section, item):
151 def backupconfig(self, section, item):
152 return (self._ocfg.backup(section, item),
152 return (self._ocfg.backup(section, item),
153 self._tcfg.backup(section, item),
153 self._tcfg.backup(section, item),
154 self._ucfg.backup(section, item),)
154 self._ucfg.backup(section, item),)
155 def restoreconfig(self, data):
155 def restoreconfig(self, data):
156 self._ocfg.restore(data[0])
156 self._ocfg.restore(data[0])
157 self._tcfg.restore(data[1])
157 self._tcfg.restore(data[1])
158 self._ucfg.restore(data[2])
158 self._ucfg.restore(data[2])
159
159
160 def setconfig(self, section, name, value):
160 def setconfig(self, section, name, value, source=''):
161 for cfg in (self._ocfg, self._tcfg, self._ucfg):
161 for cfg in (self._ocfg, self._tcfg, self._ucfg):
162 cfg.set(section, name, value)
162 cfg.set(section, name, value, source)
163 self.fixconfig(section=section)
163 self.fixconfig(section=section)
164
164
165 def _data(self, untrusted):
165 def _data(self, untrusted):
166 return untrusted and self._ucfg or self._tcfg
166 return untrusted and self._ucfg or self._tcfg
167
167
168 def configsource(self, section, name, untrusted=False):
168 def configsource(self, section, name, untrusted=False):
169 return self._data(untrusted).source(section, name) or 'none'
169 return self._data(untrusted).source(section, name) or 'none'
170
170
171 def config(self, section, name, default=None, untrusted=False):
171 def config(self, section, name, default=None, untrusted=False):
172 if isinstance(name, list):
172 if isinstance(name, list):
173 alternates = name
173 alternates = name
174 else:
174 else:
175 alternates = [name]
175 alternates = [name]
176
176
177 for n in alternates:
177 for n in alternates:
178 value = self._data(untrusted).get(section, n, None)
178 value = self._data(untrusted).get(section, n, None)
179 if value is not None:
179 if value is not None:
180 name = n
180 name = n
181 break
181 break
182 else:
182 else:
183 value = default
183 value = default
184
184
185 if self.debugflag and not untrusted and self._reportuntrusted:
185 if self.debugflag and not untrusted and self._reportuntrusted:
186 for n in alternates:
186 for n in alternates:
187 uvalue = self._ucfg.get(section, n)
187 uvalue = self._ucfg.get(section, n)
188 if uvalue is not None and uvalue != value:
188 if uvalue is not None and uvalue != value:
189 self.debug("ignoring untrusted configuration option "
189 self.debug("ignoring untrusted configuration option "
190 "%s.%s = %s\n" % (section, n, uvalue))
190 "%s.%s = %s\n" % (section, n, uvalue))
191 return value
191 return value
192
192
193 def configpath(self, section, name, default=None, untrusted=False):
193 def configpath(self, section, name, default=None, untrusted=False):
194 'get a path config item, expanded relative to repo root or config file'
194 'get a path config item, expanded relative to repo root or config file'
195 v = self.config(section, name, default, untrusted)
195 v = self.config(section, name, default, untrusted)
196 if v is None:
196 if v is None:
197 return None
197 return None
198 if not os.path.isabs(v) or "://" not in v:
198 if not os.path.isabs(v) or "://" not in v:
199 src = self.configsource(section, name, untrusted)
199 src = self.configsource(section, name, untrusted)
200 if ':' in src:
200 if ':' in src:
201 base = os.path.dirname(src.rsplit(':')[0])
201 base = os.path.dirname(src.rsplit(':')[0])
202 v = os.path.join(base, os.path.expanduser(v))
202 v = os.path.join(base, os.path.expanduser(v))
203 return v
203 return v
204
204
205 def configbool(self, section, name, default=False, untrusted=False):
205 def configbool(self, section, name, default=False, untrusted=False):
206 """parse a configuration element as a boolean
206 """parse a configuration element as a boolean
207
207
208 >>> u = ui(); s = 'foo'
208 >>> u = ui(); s = 'foo'
209 >>> u.setconfig(s, 'true', 'yes')
209 >>> u.setconfig(s, 'true', 'yes')
210 >>> u.configbool(s, 'true')
210 >>> u.configbool(s, 'true')
211 True
211 True
212 >>> u.setconfig(s, 'false', 'no')
212 >>> u.setconfig(s, 'false', 'no')
213 >>> u.configbool(s, 'false')
213 >>> u.configbool(s, 'false')
214 False
214 False
215 >>> u.configbool(s, 'unknown')
215 >>> u.configbool(s, 'unknown')
216 False
216 False
217 >>> u.configbool(s, 'unknown', True)
217 >>> u.configbool(s, 'unknown', True)
218 True
218 True
219 >>> u.setconfig(s, 'invalid', 'somevalue')
219 >>> u.setconfig(s, 'invalid', 'somevalue')
220 >>> u.configbool(s, 'invalid')
220 >>> u.configbool(s, 'invalid')
221 Traceback (most recent call last):
221 Traceback (most recent call last):
222 ...
222 ...
223 ConfigError: foo.invalid is not a boolean ('somevalue')
223 ConfigError: foo.invalid is not a boolean ('somevalue')
224 """
224 """
225
225
226 v = self.config(section, name, None, untrusted)
226 v = self.config(section, name, None, untrusted)
227 if v is None:
227 if v is None:
228 return default
228 return default
229 if isinstance(v, bool):
229 if isinstance(v, bool):
230 return v
230 return v
231 b = util.parsebool(v)
231 b = util.parsebool(v)
232 if b is None:
232 if b is None:
233 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
233 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
234 % (section, name, v))
234 % (section, name, v))
235 return b
235 return b
236
236
237 def configint(self, section, name, default=None, untrusted=False):
237 def configint(self, section, name, default=None, untrusted=False):
238 """parse a configuration element as an integer
238 """parse a configuration element as an integer
239
239
240 >>> u = ui(); s = 'foo'
240 >>> u = ui(); s = 'foo'
241 >>> u.setconfig(s, 'int1', '42')
241 >>> u.setconfig(s, 'int1', '42')
242 >>> u.configint(s, 'int1')
242 >>> u.configint(s, 'int1')
243 42
243 42
244 >>> u.setconfig(s, 'int2', '-42')
244 >>> u.setconfig(s, 'int2', '-42')
245 >>> u.configint(s, 'int2')
245 >>> u.configint(s, 'int2')
246 -42
246 -42
247 >>> u.configint(s, 'unknown', 7)
247 >>> u.configint(s, 'unknown', 7)
248 7
248 7
249 >>> u.setconfig(s, 'invalid', 'somevalue')
249 >>> u.setconfig(s, 'invalid', 'somevalue')
250 >>> u.configint(s, 'invalid')
250 >>> u.configint(s, 'invalid')
251 Traceback (most recent call last):
251 Traceback (most recent call last):
252 ...
252 ...
253 ConfigError: foo.invalid is not an integer ('somevalue')
253 ConfigError: foo.invalid is not an integer ('somevalue')
254 """
254 """
255
255
256 v = self.config(section, name, None, untrusted)
256 v = self.config(section, name, None, untrusted)
257 if v is None:
257 if v is None:
258 return default
258 return default
259 try:
259 try:
260 return int(v)
260 return int(v)
261 except ValueError:
261 except ValueError:
262 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
262 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
263 % (section, name, v))
263 % (section, name, v))
264
264
265 def configbytes(self, section, name, default=0, untrusted=False):
265 def configbytes(self, section, name, default=0, untrusted=False):
266 """parse a configuration element as a quantity in bytes
266 """parse a configuration element as a quantity in bytes
267
267
268 Units can be specified as b (bytes), k or kb (kilobytes), m or
268 Units can be specified as b (bytes), k or kb (kilobytes), m or
269 mb (megabytes), g or gb (gigabytes).
269 mb (megabytes), g or gb (gigabytes).
270
270
271 >>> u = ui(); s = 'foo'
271 >>> u = ui(); s = 'foo'
272 >>> u.setconfig(s, 'val1', '42')
272 >>> u.setconfig(s, 'val1', '42')
273 >>> u.configbytes(s, 'val1')
273 >>> u.configbytes(s, 'val1')
274 42
274 42
275 >>> u.setconfig(s, 'val2', '42.5 kb')
275 >>> u.setconfig(s, 'val2', '42.5 kb')
276 >>> u.configbytes(s, 'val2')
276 >>> u.configbytes(s, 'val2')
277 43520
277 43520
278 >>> u.configbytes(s, 'unknown', '7 MB')
278 >>> u.configbytes(s, 'unknown', '7 MB')
279 7340032
279 7340032
280 >>> u.setconfig(s, 'invalid', 'somevalue')
280 >>> u.setconfig(s, 'invalid', 'somevalue')
281 >>> u.configbytes(s, 'invalid')
281 >>> u.configbytes(s, 'invalid')
282 Traceback (most recent call last):
282 Traceback (most recent call last):
283 ...
283 ...
284 ConfigError: foo.invalid is not a byte quantity ('somevalue')
284 ConfigError: foo.invalid is not a byte quantity ('somevalue')
285 """
285 """
286
286
287 value = self.config(section, name)
287 value = self.config(section, name)
288 if value is None:
288 if value is None:
289 if not isinstance(default, str):
289 if not isinstance(default, str):
290 return default
290 return default
291 value = default
291 value = default
292 try:
292 try:
293 return util.sizetoint(value)
293 return util.sizetoint(value)
294 except error.ParseError:
294 except error.ParseError:
295 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
295 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
296 % (section, name, value))
296 % (section, name, value))
297
297
298 def configlist(self, section, name, default=None, untrusted=False):
298 def configlist(self, section, name, default=None, untrusted=False):
299 """parse a configuration element as a list of comma/space separated
299 """parse a configuration element as a list of comma/space separated
300 strings
300 strings
301
301
302 >>> u = ui(); s = 'foo'
302 >>> u = ui(); s = 'foo'
303 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
303 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
304 >>> u.configlist(s, 'list1')
304 >>> u.configlist(s, 'list1')
305 ['this', 'is', 'a small', 'test']
305 ['this', 'is', 'a small', 'test']
306 """
306 """
307
307
308 def _parse_plain(parts, s, offset):
308 def _parse_plain(parts, s, offset):
309 whitespace = False
309 whitespace = False
310 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
310 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
311 whitespace = True
311 whitespace = True
312 offset += 1
312 offset += 1
313 if offset >= len(s):
313 if offset >= len(s):
314 return None, parts, offset
314 return None, parts, offset
315 if whitespace:
315 if whitespace:
316 parts.append('')
316 parts.append('')
317 if s[offset] == '"' and not parts[-1]:
317 if s[offset] == '"' and not parts[-1]:
318 return _parse_quote, parts, offset + 1
318 return _parse_quote, parts, offset + 1
319 elif s[offset] == '"' and parts[-1][-1] == '\\':
319 elif s[offset] == '"' and parts[-1][-1] == '\\':
320 parts[-1] = parts[-1][:-1] + s[offset]
320 parts[-1] = parts[-1][:-1] + s[offset]
321 return _parse_plain, parts, offset + 1
321 return _parse_plain, parts, offset + 1
322 parts[-1] += s[offset]
322 parts[-1] += s[offset]
323 return _parse_plain, parts, offset + 1
323 return _parse_plain, parts, offset + 1
324
324
325 def _parse_quote(parts, s, offset):
325 def _parse_quote(parts, s, offset):
326 if offset < len(s) and s[offset] == '"': # ""
326 if offset < len(s) and s[offset] == '"': # ""
327 parts.append('')
327 parts.append('')
328 offset += 1
328 offset += 1
329 while offset < len(s) and (s[offset].isspace() or
329 while offset < len(s) and (s[offset].isspace() or
330 s[offset] == ','):
330 s[offset] == ','):
331 offset += 1
331 offset += 1
332 return _parse_plain, parts, offset
332 return _parse_plain, parts, offset
333
333
334 while offset < len(s) and s[offset] != '"':
334 while offset < len(s) and s[offset] != '"':
335 if (s[offset] == '\\' and offset + 1 < len(s)
335 if (s[offset] == '\\' and offset + 1 < len(s)
336 and s[offset + 1] == '"'):
336 and s[offset + 1] == '"'):
337 offset += 1
337 offset += 1
338 parts[-1] += '"'
338 parts[-1] += '"'
339 else:
339 else:
340 parts[-1] += s[offset]
340 parts[-1] += s[offset]
341 offset += 1
341 offset += 1
342
342
343 if offset >= len(s):
343 if offset >= len(s):
344 real_parts = _configlist(parts[-1])
344 real_parts = _configlist(parts[-1])
345 if not real_parts:
345 if not real_parts:
346 parts[-1] = '"'
346 parts[-1] = '"'
347 else:
347 else:
348 real_parts[0] = '"' + real_parts[0]
348 real_parts[0] = '"' + real_parts[0]
349 parts = parts[:-1]
349 parts = parts[:-1]
350 parts.extend(real_parts)
350 parts.extend(real_parts)
351 return None, parts, offset
351 return None, parts, offset
352
352
353 offset += 1
353 offset += 1
354 while offset < len(s) and s[offset] in [' ', ',']:
354 while offset < len(s) and s[offset] in [' ', ',']:
355 offset += 1
355 offset += 1
356
356
357 if offset < len(s):
357 if offset < len(s):
358 if offset + 1 == len(s) and s[offset] == '"':
358 if offset + 1 == len(s) and s[offset] == '"':
359 parts[-1] += '"'
359 parts[-1] += '"'
360 offset += 1
360 offset += 1
361 else:
361 else:
362 parts.append('')
362 parts.append('')
363 else:
363 else:
364 return None, parts, offset
364 return None, parts, offset
365
365
366 return _parse_plain, parts, offset
366 return _parse_plain, parts, offset
367
367
368 def _configlist(s):
368 def _configlist(s):
369 s = s.rstrip(' ,')
369 s = s.rstrip(' ,')
370 if not s:
370 if not s:
371 return []
371 return []
372 parser, parts, offset = _parse_plain, [''], 0
372 parser, parts, offset = _parse_plain, [''], 0
373 while parser:
373 while parser:
374 parser, parts, offset = parser(parts, s, offset)
374 parser, parts, offset = parser(parts, s, offset)
375 return parts
375 return parts
376
376
377 result = self.config(section, name, untrusted=untrusted)
377 result = self.config(section, name, untrusted=untrusted)
378 if result is None:
378 if result is None:
379 result = default or []
379 result = default or []
380 if isinstance(result, basestring):
380 if isinstance(result, basestring):
381 result = _configlist(result.lstrip(' ,\n'))
381 result = _configlist(result.lstrip(' ,\n'))
382 if result is None:
382 if result is None:
383 result = default or []
383 result = default or []
384 return result
384 return result
385
385
386 def has_section(self, section, untrusted=False):
386 def has_section(self, section, untrusted=False):
387 '''tell whether section exists in config.'''
387 '''tell whether section exists in config.'''
388 return section in self._data(untrusted)
388 return section in self._data(untrusted)
389
389
390 def configitems(self, section, untrusted=False):
390 def configitems(self, section, untrusted=False):
391 items = self._data(untrusted).items(section)
391 items = self._data(untrusted).items(section)
392 if self.debugflag and not untrusted and self._reportuntrusted:
392 if self.debugflag and not untrusted and self._reportuntrusted:
393 for k, v in self._ucfg.items(section):
393 for k, v in self._ucfg.items(section):
394 if self._tcfg.get(section, k) != v:
394 if self._tcfg.get(section, k) != v:
395 self.debug("ignoring untrusted configuration option "
395 self.debug("ignoring untrusted configuration option "
396 "%s.%s = %s\n" % (section, k, v))
396 "%s.%s = %s\n" % (section, k, v))
397 return items
397 return items
398
398
399 def walkconfig(self, untrusted=False):
399 def walkconfig(self, untrusted=False):
400 cfg = self._data(untrusted)
400 cfg = self._data(untrusted)
401 for section in cfg.sections():
401 for section in cfg.sections():
402 for name, value in self.configitems(section, untrusted):
402 for name, value in self.configitems(section, untrusted):
403 yield section, name, value
403 yield section, name, value
404
404
405 def plain(self, feature=None):
405 def plain(self, feature=None):
406 '''is plain mode active?
406 '''is plain mode active?
407
407
408 Plain mode means that all configuration variables which affect
408 Plain mode means that all configuration variables which affect
409 the behavior and output of Mercurial should be
409 the behavior and output of Mercurial should be
410 ignored. Additionally, the output should be stable,
410 ignored. Additionally, the output should be stable,
411 reproducible and suitable for use in scripts or applications.
411 reproducible and suitable for use in scripts or applications.
412
412
413 The only way to trigger plain mode is by setting either the
413 The only way to trigger plain mode is by setting either the
414 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
414 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
415
415
416 The return value can either be
416 The return value can either be
417 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
417 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
418 - True otherwise
418 - True otherwise
419 '''
419 '''
420 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
420 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
421 return False
421 return False
422 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
422 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
423 if feature and exceptions:
423 if feature and exceptions:
424 return feature not in exceptions
424 return feature not in exceptions
425 return True
425 return True
426
426
427 def username(self):
427 def username(self):
428 """Return default username to be used in commits.
428 """Return default username to be used in commits.
429
429
430 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
430 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
431 and stop searching if one of these is set.
431 and stop searching if one of these is set.
432 If not found and ui.askusername is True, ask the user, else use
432 If not found and ui.askusername is True, ask the user, else use
433 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
433 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
434 """
434 """
435 user = os.environ.get("HGUSER")
435 user = os.environ.get("HGUSER")
436 if user is None:
436 if user is None:
437 user = self.config("ui", "username")
437 user = self.config("ui", "username")
438 if user is not None:
438 if user is not None:
439 user = os.path.expandvars(user)
439 user = os.path.expandvars(user)
440 if user is None:
440 if user is None:
441 user = os.environ.get("EMAIL")
441 user = os.environ.get("EMAIL")
442 if user is None and self.configbool("ui", "askusername"):
442 if user is None and self.configbool("ui", "askusername"):
443 user = self.prompt(_("enter a commit username:"), default=None)
443 user = self.prompt(_("enter a commit username:"), default=None)
444 if user is None and not self.interactive():
444 if user is None and not self.interactive():
445 try:
445 try:
446 user = '%s@%s' % (util.getuser(), socket.getfqdn())
446 user = '%s@%s' % (util.getuser(), socket.getfqdn())
447 self.warn(_("no username found, using '%s' instead\n") % user)
447 self.warn(_("no username found, using '%s' instead\n") % user)
448 except KeyError:
448 except KeyError:
449 pass
449 pass
450 if not user:
450 if not user:
451 raise util.Abort(_('no username supplied'),
451 raise util.Abort(_('no username supplied'),
452 hint=_('use "hg config --edit" '
452 hint=_('use "hg config --edit" '
453 'to set your username'))
453 'to set your username'))
454 if "\n" in user:
454 if "\n" in user:
455 raise util.Abort(_("username %s contains a newline\n") % repr(user))
455 raise util.Abort(_("username %s contains a newline\n") % repr(user))
456 return user
456 return user
457
457
458 def shortuser(self, user):
458 def shortuser(self, user):
459 """Return a short representation of a user name or email address."""
459 """Return a short representation of a user name or email address."""
460 if not self.verbose:
460 if not self.verbose:
461 user = util.shortuser(user)
461 user = util.shortuser(user)
462 return user
462 return user
463
463
464 def expandpath(self, loc, default=None):
464 def expandpath(self, loc, default=None):
465 """Return repository location relative to cwd or from [paths]"""
465 """Return repository location relative to cwd or from [paths]"""
466 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
466 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
467 return loc
467 return loc
468
468
469 path = self.config('paths', loc)
469 path = self.config('paths', loc)
470 if not path and default is not None:
470 if not path and default is not None:
471 path = self.config('paths', default)
471 path = self.config('paths', default)
472 return path or loc
472 return path or loc
473
473
474 def pushbuffer(self):
474 def pushbuffer(self):
475 self._buffers.append([])
475 self._buffers.append([])
476
476
477 def popbuffer(self, labeled=False):
477 def popbuffer(self, labeled=False):
478 '''pop the last buffer and return the buffered output
478 '''pop the last buffer and return the buffered output
479
479
480 If labeled is True, any labels associated with buffered
480 If labeled is True, any labels associated with buffered
481 output will be handled. By default, this has no effect
481 output will be handled. By default, this has no effect
482 on the output returned, but extensions and GUI tools may
482 on the output returned, but extensions and GUI tools may
483 handle this argument and returned styled output. If output
483 handle this argument and returned styled output. If output
484 is being buffered so it can be captured and parsed or
484 is being buffered so it can be captured and parsed or
485 processed, labeled should not be set to True.
485 processed, labeled should not be set to True.
486 '''
486 '''
487 return "".join(self._buffers.pop())
487 return "".join(self._buffers.pop())
488
488
489 def write(self, *args, **opts):
489 def write(self, *args, **opts):
490 '''write args to output
490 '''write args to output
491
491
492 By default, this method simply writes to the buffer or stdout,
492 By default, this method simply writes to the buffer or stdout,
493 but extensions or GUI tools may override this method,
493 but extensions or GUI tools may override this method,
494 write_err(), popbuffer(), and label() to style output from
494 write_err(), popbuffer(), and label() to style output from
495 various parts of hg.
495 various parts of hg.
496
496
497 An optional keyword argument, "label", can be passed in.
497 An optional keyword argument, "label", can be passed in.
498 This should be a string containing label names separated by
498 This should be a string containing label names separated by
499 space. Label names take the form of "topic.type". For example,
499 space. Label names take the form of "topic.type". For example,
500 ui.debug() issues a label of "ui.debug".
500 ui.debug() issues a label of "ui.debug".
501
501
502 When labeling output for a specific command, a label of
502 When labeling output for a specific command, a label of
503 "cmdname.type" is recommended. For example, status issues
503 "cmdname.type" is recommended. For example, status issues
504 a label of "status.modified" for modified files.
504 a label of "status.modified" for modified files.
505 '''
505 '''
506 if self._buffers:
506 if self._buffers:
507 self._buffers[-1].extend([str(a) for a in args])
507 self._buffers[-1].extend([str(a) for a in args])
508 else:
508 else:
509 for a in args:
509 for a in args:
510 self.fout.write(str(a))
510 self.fout.write(str(a))
511
511
512 def write_err(self, *args, **opts):
512 def write_err(self, *args, **opts):
513 try:
513 try:
514 if not getattr(self.fout, 'closed', False):
514 if not getattr(self.fout, 'closed', False):
515 self.fout.flush()
515 self.fout.flush()
516 for a in args:
516 for a in args:
517 self.ferr.write(str(a))
517 self.ferr.write(str(a))
518 # stderr may be buffered under win32 when redirected to files,
518 # stderr may be buffered under win32 when redirected to files,
519 # including stdout.
519 # including stdout.
520 if not getattr(self.ferr, 'closed', False):
520 if not getattr(self.ferr, 'closed', False):
521 self.ferr.flush()
521 self.ferr.flush()
522 except IOError, inst:
522 except IOError, inst:
523 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
523 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
524 raise
524 raise
525
525
526 def flush(self):
526 def flush(self):
527 try: self.fout.flush()
527 try: self.fout.flush()
528 except (IOError, ValueError): pass
528 except (IOError, ValueError): pass
529 try: self.ferr.flush()
529 try: self.ferr.flush()
530 except (IOError, ValueError): pass
530 except (IOError, ValueError): pass
531
531
532 def _isatty(self, fh):
532 def _isatty(self, fh):
533 if self.configbool('ui', 'nontty', False):
533 if self.configbool('ui', 'nontty', False):
534 return False
534 return False
535 return util.isatty(fh)
535 return util.isatty(fh)
536
536
537 def interactive(self):
537 def interactive(self):
538 '''is interactive input allowed?
538 '''is interactive input allowed?
539
539
540 An interactive session is a session where input can be reasonably read
540 An interactive session is a session where input can be reasonably read
541 from `sys.stdin'. If this function returns false, any attempt to read
541 from `sys.stdin'. If this function returns false, any attempt to read
542 from stdin should fail with an error, unless a sensible default has been
542 from stdin should fail with an error, unless a sensible default has been
543 specified.
543 specified.
544
544
545 Interactiveness is triggered by the value of the `ui.interactive'
545 Interactiveness is triggered by the value of the `ui.interactive'
546 configuration variable or - if it is unset - when `sys.stdin' points
546 configuration variable or - if it is unset - when `sys.stdin' points
547 to a terminal device.
547 to a terminal device.
548
548
549 This function refers to input only; for output, see `ui.formatted()'.
549 This function refers to input only; for output, see `ui.formatted()'.
550 '''
550 '''
551 i = self.configbool("ui", "interactive", None)
551 i = self.configbool("ui", "interactive", None)
552 if i is None:
552 if i is None:
553 # some environments replace stdin without implementing isatty
553 # some environments replace stdin without implementing isatty
554 # usually those are non-interactive
554 # usually those are non-interactive
555 return self._isatty(self.fin)
555 return self._isatty(self.fin)
556
556
557 return i
557 return i
558
558
559 def termwidth(self):
559 def termwidth(self):
560 '''how wide is the terminal in columns?
560 '''how wide is the terminal in columns?
561 '''
561 '''
562 if 'COLUMNS' in os.environ:
562 if 'COLUMNS' in os.environ:
563 try:
563 try:
564 return int(os.environ['COLUMNS'])
564 return int(os.environ['COLUMNS'])
565 except ValueError:
565 except ValueError:
566 pass
566 pass
567 return util.termwidth()
567 return util.termwidth()
568
568
569 def formatted(self):
569 def formatted(self):
570 '''should formatted output be used?
570 '''should formatted output be used?
571
571
572 It is often desirable to format the output to suite the output medium.
572 It is often desirable to format the output to suite the output medium.
573 Examples of this are truncating long lines or colorizing messages.
573 Examples of this are truncating long lines or colorizing messages.
574 However, this is not often not desirable when piping output into other
574 However, this is not often not desirable when piping output into other
575 utilities, e.g. `grep'.
575 utilities, e.g. `grep'.
576
576
577 Formatted output is triggered by the value of the `ui.formatted'
577 Formatted output is triggered by the value of the `ui.formatted'
578 configuration variable or - if it is unset - when `sys.stdout' points
578 configuration variable or - if it is unset - when `sys.stdout' points
579 to a terminal device. Please note that `ui.formatted' should be
579 to a terminal device. Please note that `ui.formatted' should be
580 considered an implementation detail; it is not intended for use outside
580 considered an implementation detail; it is not intended for use outside
581 Mercurial or its extensions.
581 Mercurial or its extensions.
582
582
583 This function refers to output only; for input, see `ui.interactive()'.
583 This function refers to output only; for input, see `ui.interactive()'.
584 This function always returns false when in plain mode, see `ui.plain()'.
584 This function always returns false when in plain mode, see `ui.plain()'.
585 '''
585 '''
586 if self.plain():
586 if self.plain():
587 return False
587 return False
588
588
589 i = self.configbool("ui", "formatted", None)
589 i = self.configbool("ui", "formatted", None)
590 if i is None:
590 if i is None:
591 # some environments replace stdout without implementing isatty
591 # some environments replace stdout without implementing isatty
592 # usually those are non-interactive
592 # usually those are non-interactive
593 return self._isatty(self.fout)
593 return self._isatty(self.fout)
594
594
595 return i
595 return i
596
596
597 def _readline(self, prompt=''):
597 def _readline(self, prompt=''):
598 if self._isatty(self.fin):
598 if self._isatty(self.fin):
599 try:
599 try:
600 # magically add command line editing support, where
600 # magically add command line editing support, where
601 # available
601 # available
602 import readline
602 import readline
603 # force demandimport to really load the module
603 # force demandimport to really load the module
604 readline.read_history_file
604 readline.read_history_file
605 # windows sometimes raises something other than ImportError
605 # windows sometimes raises something other than ImportError
606 except Exception:
606 except Exception:
607 pass
607 pass
608
608
609 # call write() so output goes through subclassed implementation
609 # call write() so output goes through subclassed implementation
610 # e.g. color extension on Windows
610 # e.g. color extension on Windows
611 self.write(prompt)
611 self.write(prompt)
612
612
613 # instead of trying to emulate raw_input, swap (self.fin,
613 # instead of trying to emulate raw_input, swap (self.fin,
614 # self.fout) with (sys.stdin, sys.stdout)
614 # self.fout) with (sys.stdin, sys.stdout)
615 oldin = sys.stdin
615 oldin = sys.stdin
616 oldout = sys.stdout
616 oldout = sys.stdout
617 sys.stdin = self.fin
617 sys.stdin = self.fin
618 sys.stdout = self.fout
618 sys.stdout = self.fout
619 line = raw_input(' ')
619 line = raw_input(' ')
620 sys.stdin = oldin
620 sys.stdin = oldin
621 sys.stdout = oldout
621 sys.stdout = oldout
622
622
623 # When stdin is in binary mode on Windows, it can cause
623 # When stdin is in binary mode on Windows, it can cause
624 # raw_input() to emit an extra trailing carriage return
624 # raw_input() to emit an extra trailing carriage return
625 if os.linesep == '\r\n' and line and line[-1] == '\r':
625 if os.linesep == '\r\n' and line and line[-1] == '\r':
626 line = line[:-1]
626 line = line[:-1]
627 return line
627 return line
628
628
629 def prompt(self, msg, default="y"):
629 def prompt(self, msg, default="y"):
630 """Prompt user with msg, read response.
630 """Prompt user with msg, read response.
631 If ui is not interactive, the default is returned.
631 If ui is not interactive, the default is returned.
632 """
632 """
633 if not self.interactive():
633 if not self.interactive():
634 self.write(msg, ' ', default, "\n")
634 self.write(msg, ' ', default, "\n")
635 return default
635 return default
636 try:
636 try:
637 r = self._readline(self.label(msg, 'ui.prompt'))
637 r = self._readline(self.label(msg, 'ui.prompt'))
638 if not r:
638 if not r:
639 return default
639 return default
640 return r
640 return r
641 except EOFError:
641 except EOFError:
642 raise util.Abort(_('response expected'))
642 raise util.Abort(_('response expected'))
643
643
644 @staticmethod
644 @staticmethod
645 def extractchoices(prompt):
645 def extractchoices(prompt):
646 """Extract prompt message and list of choices from specified prompt.
646 """Extract prompt message and list of choices from specified prompt.
647
647
648 This returns tuple "(message, choices)", and "choices" is the
648 This returns tuple "(message, choices)", and "choices" is the
649 list of tuple "(response character, text without &)".
649 list of tuple "(response character, text without &)".
650 """
650 """
651 parts = prompt.split('$$')
651 parts = prompt.split('$$')
652 msg = parts[0].rstrip(' ')
652 msg = parts[0].rstrip(' ')
653 choices = [p.strip(' ') for p in parts[1:]]
653 choices = [p.strip(' ') for p in parts[1:]]
654 return (msg,
654 return (msg,
655 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
655 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
656 for s in choices])
656 for s in choices])
657
657
658 def promptchoice(self, prompt, default=0):
658 def promptchoice(self, prompt, default=0):
659 """Prompt user with a message, read response, and ensure it matches
659 """Prompt user with a message, read response, and ensure it matches
660 one of the provided choices. The prompt is formatted as follows:
660 one of the provided choices. The prompt is formatted as follows:
661
661
662 "would you like fries with that (Yn)? $$ &Yes $$ &No"
662 "would you like fries with that (Yn)? $$ &Yes $$ &No"
663
663
664 The index of the choice is returned. Responses are case
664 The index of the choice is returned. Responses are case
665 insensitive. If ui is not interactive, the default is
665 insensitive. If ui is not interactive, the default is
666 returned.
666 returned.
667 """
667 """
668
668
669 msg, choices = self.extractchoices(prompt)
669 msg, choices = self.extractchoices(prompt)
670 resps = [r for r, t in choices]
670 resps = [r for r, t in choices]
671 while True:
671 while True:
672 r = self.prompt(msg, resps[default])
672 r = self.prompt(msg, resps[default])
673 if r.lower() in resps:
673 if r.lower() in resps:
674 return resps.index(r.lower())
674 return resps.index(r.lower())
675 self.write(_("unrecognized response\n"))
675 self.write(_("unrecognized response\n"))
676
676
677 def getpass(self, prompt=None, default=None):
677 def getpass(self, prompt=None, default=None):
678 if not self.interactive():
678 if not self.interactive():
679 return default
679 return default
680 try:
680 try:
681 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
681 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
682 return getpass.getpass('')
682 return getpass.getpass('')
683 except EOFError:
683 except EOFError:
684 raise util.Abort(_('response expected'))
684 raise util.Abort(_('response expected'))
685 def status(self, *msg, **opts):
685 def status(self, *msg, **opts):
686 '''write status message to output (if ui.quiet is False)
686 '''write status message to output (if ui.quiet is False)
687
687
688 This adds an output label of "ui.status".
688 This adds an output label of "ui.status".
689 '''
689 '''
690 if not self.quiet:
690 if not self.quiet:
691 opts['label'] = opts.get('label', '') + ' ui.status'
691 opts['label'] = opts.get('label', '') + ' ui.status'
692 self.write(*msg, **opts)
692 self.write(*msg, **opts)
693 def warn(self, *msg, **opts):
693 def warn(self, *msg, **opts):
694 '''write warning message to output (stderr)
694 '''write warning message to output (stderr)
695
695
696 This adds an output label of "ui.warning".
696 This adds an output label of "ui.warning".
697 '''
697 '''
698 opts['label'] = opts.get('label', '') + ' ui.warning'
698 opts['label'] = opts.get('label', '') + ' ui.warning'
699 self.write_err(*msg, **opts)
699 self.write_err(*msg, **opts)
700 def note(self, *msg, **opts):
700 def note(self, *msg, **opts):
701 '''write note to output (if ui.verbose is True)
701 '''write note to output (if ui.verbose is True)
702
702
703 This adds an output label of "ui.note".
703 This adds an output label of "ui.note".
704 '''
704 '''
705 if self.verbose:
705 if self.verbose:
706 opts['label'] = opts.get('label', '') + ' ui.note'
706 opts['label'] = opts.get('label', '') + ' ui.note'
707 self.write(*msg, **opts)
707 self.write(*msg, **opts)
708 def debug(self, *msg, **opts):
708 def debug(self, *msg, **opts):
709 '''write debug message to output (if ui.debugflag is True)
709 '''write debug message to output (if ui.debugflag is True)
710
710
711 This adds an output label of "ui.debug".
711 This adds an output label of "ui.debug".
712 '''
712 '''
713 if self.debugflag:
713 if self.debugflag:
714 opts['label'] = opts.get('label', '') + ' ui.debug'
714 opts['label'] = opts.get('label', '') + ' ui.debug'
715 self.write(*msg, **opts)
715 self.write(*msg, **opts)
716 def edit(self, text, user, extra={}):
716 def edit(self, text, user, extra={}):
717 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
717 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
718 text=True)
718 text=True)
719 try:
719 try:
720 f = os.fdopen(fd, "w")
720 f = os.fdopen(fd, "w")
721 f.write(text)
721 f.write(text)
722 f.close()
722 f.close()
723
723
724 environ = {'HGUSER': user}
724 environ = {'HGUSER': user}
725 if 'transplant_source' in extra:
725 if 'transplant_source' in extra:
726 environ.update({'HGREVISION': hex(extra['transplant_source'])})
726 environ.update({'HGREVISION': hex(extra['transplant_source'])})
727 for label in ('source', 'rebase_source'):
727 for label in ('source', 'rebase_source'):
728 if label in extra:
728 if label in extra:
729 environ.update({'HGREVISION': extra[label]})
729 environ.update({'HGREVISION': extra[label]})
730 break
730 break
731
731
732 editor = self.geteditor()
732 editor = self.geteditor()
733
733
734 util.system("%s \"%s\"" % (editor, name),
734 util.system("%s \"%s\"" % (editor, name),
735 environ=environ,
735 environ=environ,
736 onerr=util.Abort, errprefix=_("edit failed"),
736 onerr=util.Abort, errprefix=_("edit failed"),
737 out=self.fout)
737 out=self.fout)
738
738
739 f = open(name)
739 f = open(name)
740 t = f.read()
740 t = f.read()
741 f.close()
741 f.close()
742 finally:
742 finally:
743 os.unlink(name)
743 os.unlink(name)
744
744
745 return t
745 return t
746
746
747 def traceback(self, exc=None, force=False):
747 def traceback(self, exc=None, force=False):
748 '''print exception traceback if traceback printing enabled or forced.
748 '''print exception traceback if traceback printing enabled or forced.
749 only to call in exception handler. returns true if traceback
749 only to call in exception handler. returns true if traceback
750 printed.'''
750 printed.'''
751 if self.tracebackflag or force:
751 if self.tracebackflag or force:
752 if exc is None:
752 if exc is None:
753 exc = sys.exc_info()
753 exc = sys.exc_info()
754 cause = getattr(exc[1], 'cause', None)
754 cause = getattr(exc[1], 'cause', None)
755
755
756 if cause is not None:
756 if cause is not None:
757 causetb = traceback.format_tb(cause[2])
757 causetb = traceback.format_tb(cause[2])
758 exctb = traceback.format_tb(exc[2])
758 exctb = traceback.format_tb(exc[2])
759 exconly = traceback.format_exception_only(cause[0], cause[1])
759 exconly = traceback.format_exception_only(cause[0], cause[1])
760
760
761 # exclude frame where 'exc' was chained and rethrown from exctb
761 # exclude frame where 'exc' was chained and rethrown from exctb
762 self.write_err('Traceback (most recent call last):\n',
762 self.write_err('Traceback (most recent call last):\n',
763 ''.join(exctb[:-1]),
763 ''.join(exctb[:-1]),
764 ''.join(causetb),
764 ''.join(causetb),
765 ''.join(exconly))
765 ''.join(exconly))
766 else:
766 else:
767 traceback.print_exception(exc[0], exc[1], exc[2],
767 traceback.print_exception(exc[0], exc[1], exc[2],
768 file=self.ferr)
768 file=self.ferr)
769 return self.tracebackflag or force
769 return self.tracebackflag or force
770
770
771 def geteditor(self):
771 def geteditor(self):
772 '''return editor to use'''
772 '''return editor to use'''
773 if sys.platform == 'plan9':
773 if sys.platform == 'plan9':
774 # vi is the MIPS instruction simulator on Plan 9. We
774 # vi is the MIPS instruction simulator on Plan 9. We
775 # instead default to E to plumb commit messages to
775 # instead default to E to plumb commit messages to
776 # avoid confusion.
776 # avoid confusion.
777 editor = 'E'
777 editor = 'E'
778 else:
778 else:
779 editor = 'vi'
779 editor = 'vi'
780 return (os.environ.get("HGEDITOR") or
780 return (os.environ.get("HGEDITOR") or
781 self.config("ui", "editor") or
781 self.config("ui", "editor") or
782 os.environ.get("VISUAL") or
782 os.environ.get("VISUAL") or
783 os.environ.get("EDITOR", editor))
783 os.environ.get("EDITOR", editor))
784
784
785 def progress(self, topic, pos, item="", unit="", total=None):
785 def progress(self, topic, pos, item="", unit="", total=None):
786 '''show a progress message
786 '''show a progress message
787
787
788 With stock hg, this is simply a debug message that is hidden
788 With stock hg, this is simply a debug message that is hidden
789 by default, but with extensions or GUI tools it may be
789 by default, but with extensions or GUI tools it may be
790 visible. 'topic' is the current operation, 'item' is a
790 visible. 'topic' is the current operation, 'item' is a
791 non-numeric marker of the current position (i.e. the currently
791 non-numeric marker of the current position (i.e. the currently
792 in-process file), 'pos' is the current numeric position (i.e.
792 in-process file), 'pos' is the current numeric position (i.e.
793 revision, bytes, etc.), unit is a corresponding unit label,
793 revision, bytes, etc.), unit is a corresponding unit label,
794 and total is the highest expected pos.
794 and total is the highest expected pos.
795
795
796 Multiple nested topics may be active at a time.
796 Multiple nested topics may be active at a time.
797
797
798 All topics should be marked closed by setting pos to None at
798 All topics should be marked closed by setting pos to None at
799 termination.
799 termination.
800 '''
800 '''
801
801
802 if pos is None or not self.debugflag:
802 if pos is None or not self.debugflag:
803 return
803 return
804
804
805 if unit:
805 if unit:
806 unit = ' ' + unit
806 unit = ' ' + unit
807 if item:
807 if item:
808 item = ' ' + item
808 item = ' ' + item
809
809
810 if total:
810 if total:
811 pct = 100.0 * pos / total
811 pct = 100.0 * pos / total
812 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
812 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
813 % (topic, item, pos, total, unit, pct))
813 % (topic, item, pos, total, unit, pct))
814 else:
814 else:
815 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
815 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
816
816
817 def log(self, service, *msg, **opts):
817 def log(self, service, *msg, **opts):
818 '''hook for logging facility extensions
818 '''hook for logging facility extensions
819
819
820 service should be a readily-identifiable subsystem, which will
820 service should be a readily-identifiable subsystem, which will
821 allow filtering.
821 allow filtering.
822 message should be a newline-terminated string to log.
822 message should be a newline-terminated string to log.
823 '''
823 '''
824 pass
824 pass
825
825
826 def label(self, msg, label):
826 def label(self, msg, label):
827 '''style msg based on supplied label
827 '''style msg based on supplied label
828
828
829 Like ui.write(), this just returns msg unchanged, but extensions
829 Like ui.write(), this just returns msg unchanged, but extensions
830 and GUI tools can override it to allow styling output without
830 and GUI tools can override it to allow styling output without
831 writing it.
831 writing it.
832
832
833 ui.write(s, 'label') is equivalent to
833 ui.write(s, 'label') is equivalent to
834 ui.write(ui.label(s, 'label')).
834 ui.write(ui.label(s, 'label')).
835 '''
835 '''
836 return msg
836 return msg
@@ -1,203 +1,203 b''
1 hide outer repo
1 hide outer repo
2 $ hg init
2 $ hg init
3
3
4 Use hgrc within $TESTTMP
4 Use hgrc within $TESTTMP
5
5
6 $ HGRCPATH=`pwd`/hgrc
6 $ HGRCPATH=`pwd`/hgrc
7 $ export HGRCPATH
7 $ export HGRCPATH
8
8
9 Use an alternate var for scribbling on hgrc to keep check-code from
9 Use an alternate var for scribbling on hgrc to keep check-code from
10 complaining about the important settings we may be overwriting:
10 complaining about the important settings we may be overwriting:
11
11
12 $ HGRC=`pwd`/hgrc
12 $ HGRC=`pwd`/hgrc
13 $ export HGRC
13 $ export HGRC
14
14
15 Basic syntax error
15 Basic syntax error
16
16
17 $ echo "invalid" > $HGRC
17 $ echo "invalid" > $HGRC
18 $ hg version
18 $ hg version
19 hg: parse error at $TESTTMP/hgrc:1: invalid
19 hg: parse error at $TESTTMP/hgrc:1: invalid
20 [255]
20 [255]
21 $ echo "" > $HGRC
21 $ echo "" > $HGRC
22
22
23 Issue1199: Can't use '%' in hgrc (eg url encoded username)
23 Issue1199: Can't use '%' in hgrc (eg url encoded username)
24
24
25 $ hg init "foo%bar"
25 $ hg init "foo%bar"
26 $ hg clone "foo%bar" foobar
26 $ hg clone "foo%bar" foobar
27 updating to branch default
27 updating to branch default
28 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
28 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
29 $ cd foobar
29 $ cd foobar
30 $ cat .hg/hgrc
30 $ cat .hg/hgrc
31 [paths]
31 [paths]
32 default = $TESTTMP/foo%bar (glob)
32 default = $TESTTMP/foo%bar (glob)
33 $ hg paths
33 $ hg paths
34 default = $TESTTMP/foo%bar (glob)
34 default = $TESTTMP/foo%bar (glob)
35 $ hg showconfig
35 $ hg showconfig
36 bundle.mainreporoot=$TESTTMP/foobar (glob)
36 bundle.mainreporoot=$TESTTMP/foobar (glob)
37 paths.default=$TESTTMP/foo%bar (glob)
37 paths.default=$TESTTMP/foo%bar (glob)
38 $ cd ..
38 $ cd ..
39
39
40 issue1829: wrong indentation
40 issue1829: wrong indentation
41
41
42 $ echo '[foo]' > $HGRC
42 $ echo '[foo]' > $HGRC
43 $ echo ' x = y' >> $HGRC
43 $ echo ' x = y' >> $HGRC
44 $ hg version
44 $ hg version
45 hg: parse error at $TESTTMP/hgrc:2: x = y
45 hg: parse error at $TESTTMP/hgrc:2: x = y
46 [255]
46 [255]
47
47
48 $ python -c "print '[foo]\nbar = a\n b\n c \n de\n fg \nbaz = bif cb \n'" \
48 $ python -c "print '[foo]\nbar = a\n b\n c \n de\n fg \nbaz = bif cb \n'" \
49 > > $HGRC
49 > > $HGRC
50 $ hg showconfig foo
50 $ hg showconfig foo
51 foo.bar=a\nb\nc\nde\nfg
51 foo.bar=a\nb\nc\nde\nfg
52 foo.baz=bif cb
52 foo.baz=bif cb
53
53
54 $ FAKEPATH=/path/to/nowhere
54 $ FAKEPATH=/path/to/nowhere
55 $ export FAKEPATH
55 $ export FAKEPATH
56 $ echo '%include $FAKEPATH/no-such-file' > $HGRC
56 $ echo '%include $FAKEPATH/no-such-file' > $HGRC
57 $ hg version
57 $ hg version
58 Mercurial Distributed SCM (version *) (glob)
58 Mercurial Distributed SCM (version *) (glob)
59 (see http://mercurial.selenic.com for more information)
59 (see http://mercurial.selenic.com for more information)
60
60
61 Copyright (C) 2005-2014 Matt Mackall and others
61 Copyright (C) 2005-2014 Matt Mackall and others
62 This is free software; see the source for copying conditions. There is NO
62 This is free software; see the source for copying conditions. There is NO
63 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
63 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
64 $ unset FAKEPATH
64 $ unset FAKEPATH
65
65
66 make sure global options given on the cmdline take precedence
66 make sure global options given on the cmdline take precedence
67
67
68 $ hg showconfig --config ui.verbose=True --quiet
68 $ hg showconfig --config ui.verbose=True --quiet
69 bundle.mainreporoot=$TESTTMP
69 bundle.mainreporoot=$TESTTMP
70 ui.verbose=False
70 ui.verbose=False
71 ui.debug=False
71 ui.debug=False
72 ui.quiet=True
72 ui.quiet=True
73
73
74 $ touch foobar/untracked
74 $ touch foobar/untracked
75 $ cat >> foobar/.hg/hgrc <<EOF
75 $ cat >> foobar/.hg/hgrc <<EOF
76 > [ui]
76 > [ui]
77 > verbose=True
77 > verbose=True
78 > EOF
78 > EOF
79 $ hg -R foobar st -q
79 $ hg -R foobar st -q
80
80
81 username expansion
81 username expansion
82
82
83 $ olduser=$HGUSER
83 $ olduser=$HGUSER
84 $ unset HGUSER
84 $ unset HGUSER
85
85
86 $ FAKEUSER='John Doe'
86 $ FAKEUSER='John Doe'
87 $ export FAKEUSER
87 $ export FAKEUSER
88 $ echo '[ui]' > $HGRC
88 $ echo '[ui]' > $HGRC
89 $ echo 'username = $FAKEUSER' >> $HGRC
89 $ echo 'username = $FAKEUSER' >> $HGRC
90
90
91 $ hg init usertest
91 $ hg init usertest
92 $ cd usertest
92 $ cd usertest
93 $ touch bar
93 $ touch bar
94 $ hg commit --addremove --quiet -m "added bar"
94 $ hg commit --addremove --quiet -m "added bar"
95 $ hg log --template "{author}\n"
95 $ hg log --template "{author}\n"
96 John Doe
96 John Doe
97 $ cd ..
97 $ cd ..
98
98
99 $ hg showconfig
99 $ hg showconfig
100 bundle.mainreporoot=$TESTTMP
100 bundle.mainreporoot=$TESTTMP
101 ui.username=$FAKEUSER
101 ui.username=$FAKEUSER
102
102
103 $ unset FAKEUSER
103 $ unset FAKEUSER
104 $ HGUSER=$olduser
104 $ HGUSER=$olduser
105 $ export HGUSER
105 $ export HGUSER
106
106
107 showconfig with multiple arguments
107 showconfig with multiple arguments
108
108
109 $ echo "[alias]" > $HGRC
109 $ echo "[alias]" > $HGRC
110 $ echo "log = log -g" >> $HGRC
110 $ echo "log = log -g" >> $HGRC
111 $ echo "[defaults]" >> $HGRC
111 $ echo "[defaults]" >> $HGRC
112 $ echo "identify = -n" >> $HGRC
112 $ echo "identify = -n" >> $HGRC
113 $ hg showconfig alias defaults
113 $ hg showconfig alias defaults
114 alias.log=log -g
114 alias.log=log -g
115 defaults.identify=-n
115 defaults.identify=-n
116 $ hg showconfig alias defaults.identify
116 $ hg showconfig alias defaults.identify
117 abort: only one config item permitted
117 abort: only one config item permitted
118 [255]
118 [255]
119 $ hg showconfig alias.log defaults.identify
119 $ hg showconfig alias.log defaults.identify
120 abort: only one config item permitted
120 abort: only one config item permitted
121 [255]
121 [255]
122
122
123 HGPLAIN
123 HGPLAIN
124
124
125 $ echo "[ui]" > $HGRC
125 $ echo "[ui]" > $HGRC
126 $ echo "debug=true" >> $HGRC
126 $ echo "debug=true" >> $HGRC
127 $ echo "fallbackencoding=ASCII" >> $HGRC
127 $ echo "fallbackencoding=ASCII" >> $HGRC
128 $ echo "quiet=true" >> $HGRC
128 $ echo "quiet=true" >> $HGRC
129 $ echo "slash=true" >> $HGRC
129 $ echo "slash=true" >> $HGRC
130 $ echo "traceback=true" >> $HGRC
130 $ echo "traceback=true" >> $HGRC
131 $ echo "verbose=true" >> $HGRC
131 $ echo "verbose=true" >> $HGRC
132 $ echo "style=~/.hgstyle" >> $HGRC
132 $ echo "style=~/.hgstyle" >> $HGRC
133 $ echo "logtemplate={node}" >> $HGRC
133 $ echo "logtemplate={node}" >> $HGRC
134 $ echo "[defaults]" >> $HGRC
134 $ echo "[defaults]" >> $HGRC
135 $ echo "identify=-n" >> $HGRC
135 $ echo "identify=-n" >> $HGRC
136 $ echo "[alias]" >> $HGRC
136 $ echo "[alias]" >> $HGRC
137 $ echo "log=log -g" >> $HGRC
137 $ echo "log=log -g" >> $HGRC
138
138
139 customized hgrc
139 customized hgrc
140
140
141 $ hg showconfig
141 $ hg showconfig
142 read config from: $TESTTMP/hgrc
142 read config from: $TESTTMP/hgrc
143 $TESTTMP/hgrc:13: alias.log=log -g
143 $TESTTMP/hgrc:13: alias.log=log -g
144 none: bundle.mainreporoot=$TESTTMP
144 repo: bundle.mainreporoot=$TESTTMP
145 $TESTTMP/hgrc:11: defaults.identify=-n
145 $TESTTMP/hgrc:11: defaults.identify=-n
146 $TESTTMP/hgrc:2: ui.debug=true
146 $TESTTMP/hgrc:2: ui.debug=true
147 $TESTTMP/hgrc:3: ui.fallbackencoding=ASCII
147 $TESTTMP/hgrc:3: ui.fallbackencoding=ASCII
148 $TESTTMP/hgrc:4: ui.quiet=true
148 $TESTTMP/hgrc:4: ui.quiet=true
149 $TESTTMP/hgrc:5: ui.slash=true
149 $TESTTMP/hgrc:5: ui.slash=true
150 $TESTTMP/hgrc:6: ui.traceback=true
150 $TESTTMP/hgrc:6: ui.traceback=true
151 $TESTTMP/hgrc:7: ui.verbose=true
151 $TESTTMP/hgrc:7: ui.verbose=true
152 $TESTTMP/hgrc:8: ui.style=~/.hgstyle
152 $TESTTMP/hgrc:8: ui.style=~/.hgstyle
153 $TESTTMP/hgrc:9: ui.logtemplate={node}
153 $TESTTMP/hgrc:9: ui.logtemplate={node}
154
154
155 plain hgrc
155 plain hgrc
156
156
157 $ HGPLAIN=; export HGPLAIN
157 $ HGPLAIN=; export HGPLAIN
158 $ hg showconfig --config ui.traceback=True --debug
158 $ hg showconfig --config ui.traceback=True --debug
159 read config from: $TESTTMP/hgrc
159 read config from: $TESTTMP/hgrc
160 none: bundle.mainreporoot=$TESTTMP
160 repo: bundle.mainreporoot=$TESTTMP
161 none: ui.traceback=True
161 --config: ui.traceback=True
162 none: ui.verbose=False
162 --verbose: ui.verbose=False
163 none: ui.debug=True
163 --debug: ui.debug=True
164 none: ui.quiet=False
164 --quiet: ui.quiet=False
165
165
166 plain mode with exceptions
166 plain mode with exceptions
167
167
168 $ cat > plain.py <<EOF
168 $ cat > plain.py <<EOF
169 > def uisetup(ui):
169 > def uisetup(ui):
170 > ui.write('plain: %r\n' % ui.plain())
170 > ui.write('plain: %r\n' % ui.plain())
171 > EOF
171 > EOF
172 $ echo "[extensions]" >> $HGRC
172 $ echo "[extensions]" >> $HGRC
173 $ echo "plain=./plain.py" >> $HGRC
173 $ echo "plain=./plain.py" >> $HGRC
174 $ HGPLAINEXCEPT=; export HGPLAINEXCEPT
174 $ HGPLAINEXCEPT=; export HGPLAINEXCEPT
175 $ hg showconfig --config ui.traceback=True --debug
175 $ hg showconfig --config ui.traceback=True --debug
176 plain: True
176 plain: True
177 read config from: $TESTTMP/hgrc
177 read config from: $TESTTMP/hgrc
178 none: bundle.mainreporoot=$TESTTMP
178 repo: bundle.mainreporoot=$TESTTMP
179 $TESTTMP/hgrc:15: extensions.plain=./plain.py
179 $TESTTMP/hgrc:15: extensions.plain=./plain.py
180 none: ui.traceback=True
180 --config: ui.traceback=True
181 none: ui.verbose=False
181 --verbose: ui.verbose=False
182 none: ui.debug=True
182 --debug: ui.debug=True
183 none: ui.quiet=False
183 --quiet: ui.quiet=False
184 $ unset HGPLAIN
184 $ unset HGPLAIN
185 $ hg showconfig --config ui.traceback=True --debug
185 $ hg showconfig --config ui.traceback=True --debug
186 plain: True
186 plain: True
187 read config from: $TESTTMP/hgrc
187 read config from: $TESTTMP/hgrc
188 none: bundle.mainreporoot=$TESTTMP
188 repo: bundle.mainreporoot=$TESTTMP
189 $TESTTMP/hgrc:15: extensions.plain=./plain.py
189 $TESTTMP/hgrc:15: extensions.plain=./plain.py
190 none: ui.traceback=True
190 --config: ui.traceback=True
191 none: ui.verbose=False
191 --verbose: ui.verbose=False
192 none: ui.debug=True
192 --debug: ui.debug=True
193 none: ui.quiet=False
193 --quiet: ui.quiet=False
194 $ HGPLAINEXCEPT=i18n; export HGPLAINEXCEPT
194 $ HGPLAINEXCEPT=i18n; export HGPLAINEXCEPT
195 $ hg showconfig --config ui.traceback=True --debug
195 $ hg showconfig --config ui.traceback=True --debug
196 plain: True
196 plain: True
197 read config from: $TESTTMP/hgrc
197 read config from: $TESTTMP/hgrc
198 none: bundle.mainreporoot=$TESTTMP
198 repo: bundle.mainreporoot=$TESTTMP
199 $TESTTMP/hgrc:15: extensions.plain=./plain.py
199 $TESTTMP/hgrc:15: extensions.plain=./plain.py
200 none: ui.traceback=True
200 --config: ui.traceback=True
201 none: ui.verbose=False
201 --verbose: ui.verbose=False
202 none: ui.debug=True
202 --debug: ui.debug=True
203 none: ui.quiet=False
203 --quiet: ui.quiet=False
General Comments 0
You need to be logged in to leave comments. Login now