##// END OF EJS Templates
mark ui.warn strings for translation
Martin Geisler -
r11600:76454cbc stable
parent child Browse files
Show More
@@ -1,536 +1,536 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
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time
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 def run():
14 def run():
15 "run the command in sys.argv"
15 "run the command in sys.argv"
16 sys.exit(dispatch(sys.argv[1:]))
16 sys.exit(dispatch(sys.argv[1:]))
17
17
18 def dispatch(args):
18 def dispatch(args):
19 "run the command specified in args"
19 "run the command specified in args"
20 try:
20 try:
21 u = uimod.ui()
21 u = uimod.ui()
22 if '--traceback' in args:
22 if '--traceback' in args:
23 u.setconfig('ui', 'traceback', 'on')
23 u.setconfig('ui', 'traceback', 'on')
24 except util.Abort, inst:
24 except util.Abort, inst:
25 sys.stderr.write(_("abort: %s\n") % inst)
25 sys.stderr.write(_("abort: %s\n") % inst)
26 return -1
26 return -1
27 except error.ParseError, inst:
27 except error.ParseError, inst:
28 if len(inst.args) > 1:
28 if len(inst.args) > 1:
29 sys.stderr.write(_("hg: parse error at %s: %s\n") %
29 sys.stderr.write(_("hg: parse error at %s: %s\n") %
30 (inst.args[1], inst.args[0]))
30 (inst.args[1], inst.args[0]))
31 else:
31 else:
32 sys.stderr.write(_("hg: parse error: %s\n") % inst.args[0])
32 sys.stderr.write(_("hg: parse error: %s\n") % inst.args[0])
33 return -1
33 return -1
34 return _runcatch(u, args)
34 return _runcatch(u, args)
35
35
36 def _runcatch(ui, args):
36 def _runcatch(ui, args):
37 def catchterm(*args):
37 def catchterm(*args):
38 raise error.SignalInterrupt
38 raise error.SignalInterrupt
39
39
40 try:
40 try:
41 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
41 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
42 num = getattr(signal, name, None)
42 num = getattr(signal, name, None)
43 if num:
43 if num:
44 signal.signal(num, catchterm)
44 signal.signal(num, catchterm)
45 except ValueError:
45 except ValueError:
46 pass # happens if called in a thread
46 pass # happens if called in a thread
47
47
48 try:
48 try:
49 try:
49 try:
50 # enter the debugger before command execution
50 # enter the debugger before command execution
51 if '--debugger' in args:
51 if '--debugger' in args:
52 pdb.set_trace()
52 pdb.set_trace()
53 try:
53 try:
54 return _dispatch(ui, args)
54 return _dispatch(ui, args)
55 finally:
55 finally:
56 ui.flush()
56 ui.flush()
57 except:
57 except:
58 # enter the debugger when we hit an exception
58 # enter the debugger when we hit an exception
59 if '--debugger' in args:
59 if '--debugger' in args:
60 pdb.post_mortem(sys.exc_info()[2])
60 pdb.post_mortem(sys.exc_info()[2])
61 ui.traceback()
61 ui.traceback()
62 raise
62 raise
63
63
64 # Global exception handling, alphabetically
64 # Global exception handling, alphabetically
65 # Mercurial-specific first, followed by built-in and library exceptions
65 # Mercurial-specific first, followed by built-in and library exceptions
66 except error.AmbiguousCommand, inst:
66 except error.AmbiguousCommand, inst:
67 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
67 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
68 (inst.args[0], " ".join(inst.args[1])))
68 (inst.args[0], " ".join(inst.args[1])))
69 except error.ParseError, inst:
69 except error.ParseError, inst:
70 if len(inst.args) > 1:
70 if len(inst.args) > 1:
71 ui.warn(_("hg: parse error at %s: %s\n") %
71 ui.warn(_("hg: parse error at %s: %s\n") %
72 (inst.args[1], inst.args[0]))
72 (inst.args[1], inst.args[0]))
73 else:
73 else:
74 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
74 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
75 return -1
75 return -1
76 except error.LockHeld, inst:
76 except error.LockHeld, inst:
77 if inst.errno == errno.ETIMEDOUT:
77 if inst.errno == errno.ETIMEDOUT:
78 reason = _('timed out waiting for lock held by %s') % inst.locker
78 reason = _('timed out waiting for lock held by %s') % inst.locker
79 else:
79 else:
80 reason = _('lock held by %s') % inst.locker
80 reason = _('lock held by %s') % inst.locker
81 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
81 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
82 except error.LockUnavailable, inst:
82 except error.LockUnavailable, inst:
83 ui.warn(_("abort: could not lock %s: %s\n") %
83 ui.warn(_("abort: could not lock %s: %s\n") %
84 (inst.desc or inst.filename, inst.strerror))
84 (inst.desc or inst.filename, inst.strerror))
85 except error.CommandError, inst:
85 except error.CommandError, inst:
86 if inst.args[0]:
86 if inst.args[0]:
87 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
87 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
88 commands.help_(ui, inst.args[0])
88 commands.help_(ui, inst.args[0])
89 else:
89 else:
90 ui.warn(_("hg: %s\n") % inst.args[1])
90 ui.warn(_("hg: %s\n") % inst.args[1])
91 commands.help_(ui, 'shortlist')
91 commands.help_(ui, 'shortlist')
92 except error.RepoError, inst:
92 except error.RepoError, inst:
93 ui.warn(_("abort: %s!\n") % inst)
93 ui.warn(_("abort: %s!\n") % inst)
94 except error.ResponseError, inst:
94 except error.ResponseError, inst:
95 ui.warn(_("abort: %s") % inst.args[0])
95 ui.warn(_("abort: %s") % inst.args[0])
96 if not isinstance(inst.args[1], basestring):
96 if not isinstance(inst.args[1], basestring):
97 ui.warn(" %r\n" % (inst.args[1],))
97 ui.warn(" %r\n" % (inst.args[1],))
98 elif not inst.args[1]:
98 elif not inst.args[1]:
99 ui.warn(_(" empty string\n"))
99 ui.warn(_(" empty string\n"))
100 else:
100 else:
101 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
101 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
102 except error.RevlogError, inst:
102 except error.RevlogError, inst:
103 ui.warn(_("abort: %s!\n") % inst)
103 ui.warn(_("abort: %s!\n") % inst)
104 except error.SignalInterrupt:
104 except error.SignalInterrupt:
105 ui.warn(_("killed!\n"))
105 ui.warn(_("killed!\n"))
106 except error.UnknownCommand, inst:
106 except error.UnknownCommand, inst:
107 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
107 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
108 try:
108 try:
109 # check if the command is in a disabled extension
109 # check if the command is in a disabled extension
110 # (but don't check for extensions themselves)
110 # (but don't check for extensions themselves)
111 commands.help_(ui, inst.args[0], unknowncmd=True)
111 commands.help_(ui, inst.args[0], unknowncmd=True)
112 except error.UnknownCommand:
112 except error.UnknownCommand:
113 commands.help_(ui, 'shortlist')
113 commands.help_(ui, 'shortlist')
114 except util.Abort, inst:
114 except util.Abort, inst:
115 ui.warn(_("abort: %s\n") % inst)
115 ui.warn(_("abort: %s\n") % inst)
116 except ImportError, inst:
116 except ImportError, inst:
117 ui.warn(_("abort: %s!\n") % inst)
117 ui.warn(_("abort: %s!\n") % inst)
118 m = str(inst).split()[-1]
118 m = str(inst).split()[-1]
119 if m in "mpatch bdiff".split():
119 if m in "mpatch bdiff".split():
120 ui.warn(_("(did you forget to compile extensions?)\n"))
120 ui.warn(_("(did you forget to compile extensions?)\n"))
121 elif m in "zlib".split():
121 elif m in "zlib".split():
122 ui.warn(_("(is your Python install correct?)\n"))
122 ui.warn(_("(is your Python install correct?)\n"))
123 except IOError, inst:
123 except IOError, inst:
124 if hasattr(inst, "code"):
124 if hasattr(inst, "code"):
125 ui.warn(_("abort: %s\n") % inst)
125 ui.warn(_("abort: %s\n") % inst)
126 elif hasattr(inst, "reason"):
126 elif hasattr(inst, "reason"):
127 try: # usually it is in the form (errno, strerror)
127 try: # usually it is in the form (errno, strerror)
128 reason = inst.reason.args[1]
128 reason = inst.reason.args[1]
129 except: # it might be anything, for example a string
129 except: # it might be anything, for example a string
130 reason = inst.reason
130 reason = inst.reason
131 ui.warn(_("abort: error: %s\n") % reason)
131 ui.warn(_("abort: error: %s\n") % reason)
132 elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
132 elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
133 if ui.debugflag:
133 if ui.debugflag:
134 ui.warn(_("broken pipe\n"))
134 ui.warn(_("broken pipe\n"))
135 elif getattr(inst, "strerror", None):
135 elif getattr(inst, "strerror", None):
136 if getattr(inst, "filename", None):
136 if getattr(inst, "filename", None):
137 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
137 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
138 else:
138 else:
139 ui.warn(_("abort: %s\n") % inst.strerror)
139 ui.warn(_("abort: %s\n") % inst.strerror)
140 else:
140 else:
141 raise
141 raise
142 except OSError, inst:
142 except OSError, inst:
143 if getattr(inst, "filename", None):
143 if getattr(inst, "filename", None):
144 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
144 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
145 else:
145 else:
146 ui.warn(_("abort: %s\n") % inst.strerror)
146 ui.warn(_("abort: %s\n") % inst.strerror)
147 except KeyboardInterrupt:
147 except KeyboardInterrupt:
148 try:
148 try:
149 ui.warn(_("interrupted!\n"))
149 ui.warn(_("interrupted!\n"))
150 except IOError, inst:
150 except IOError, inst:
151 if inst.errno == errno.EPIPE:
151 if inst.errno == errno.EPIPE:
152 if ui.debugflag:
152 if ui.debugflag:
153 ui.warn(_("\nbroken pipe\n"))
153 ui.warn(_("\nbroken pipe\n"))
154 else:
154 else:
155 raise
155 raise
156 except MemoryError:
156 except MemoryError:
157 ui.warn(_("abort: out of memory\n"))
157 ui.warn(_("abort: out of memory\n"))
158 except SystemExit, inst:
158 except SystemExit, inst:
159 # Commands shouldn't sys.exit directly, but give a return code.
159 # Commands shouldn't sys.exit directly, but give a return code.
160 # Just in case catch this and and pass exit code to caller.
160 # Just in case catch this and and pass exit code to caller.
161 return inst.code
161 return inst.code
162 except socket.error, inst:
162 except socket.error, inst:
163 ui.warn(_("abort: %s\n") % inst.args[-1])
163 ui.warn(_("abort: %s\n") % inst.args[-1])
164 except:
164 except:
165 ui.warn(_("** unknown exception encountered, details follow\n"))
165 ui.warn(_("** unknown exception encountered, details follow\n"))
166 ui.warn(_("** report bug details to "
166 ui.warn(_("** report bug details to "
167 "http://mercurial.selenic.com/bts/\n"))
167 "http://mercurial.selenic.com/bts/\n"))
168 ui.warn(_("** or mercurial@selenic.com\n"))
168 ui.warn(_("** or mercurial@selenic.com\n"))
169 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
169 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
170 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
170 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
171 % util.version())
171 % util.version())
172 ui.warn(_("** Extensions loaded: %s\n")
172 ui.warn(_("** Extensions loaded: %s\n")
173 % ", ".join([x[0] for x in extensions.extensions()]))
173 % ", ".join([x[0] for x in extensions.extensions()]))
174 raise
174 raise
175
175
176 return -1
176 return -1
177
177
178 def aliasargs(fn):
178 def aliasargs(fn):
179 if hasattr(fn, 'args'):
179 if hasattr(fn, 'args'):
180 return fn.args
180 return fn.args
181 return []
181 return []
182
182
183 class cmdalias(object):
183 class cmdalias(object):
184 def __init__(self, name, definition, cmdtable):
184 def __init__(self, name, definition, cmdtable):
185 self.name = name
185 self.name = name
186 self.definition = definition
186 self.definition = definition
187 self.args = []
187 self.args = []
188 self.opts = []
188 self.opts = []
189 self.help = ''
189 self.help = ''
190 self.norepo = True
190 self.norepo = True
191 self.badalias = False
191 self.badalias = False
192
192
193 try:
193 try:
194 cmdutil.findcmd(self.name, cmdtable, True)
194 cmdutil.findcmd(self.name, cmdtable, True)
195 self.shadows = True
195 self.shadows = True
196 except error.UnknownCommand:
196 except error.UnknownCommand:
197 self.shadows = False
197 self.shadows = False
198
198
199 if not self.definition:
199 if not self.definition:
200 def fn(ui, *args):
200 def fn(ui, *args):
201 ui.warn(_("no definition for alias '%s'\n") % self.name)
201 ui.warn(_("no definition for alias '%s'\n") % self.name)
202 return 1
202 return 1
203 self.fn = fn
203 self.fn = fn
204 self.badalias = True
204 self.badalias = True
205
205
206 return
206 return
207
207
208 args = shlex.split(self.definition)
208 args = shlex.split(self.definition)
209 cmd = args.pop(0)
209 cmd = args.pop(0)
210 args = map(util.expandpath, args)
210 args = map(util.expandpath, args)
211
211
212 try:
212 try:
213 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
213 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
214 if len(tableentry) > 2:
214 if len(tableentry) > 2:
215 self.fn, self.opts, self.help = tableentry
215 self.fn, self.opts, self.help = tableentry
216 else:
216 else:
217 self.fn, self.opts = tableentry
217 self.fn, self.opts = tableentry
218
218
219 self.args = aliasargs(self.fn) + args
219 self.args = aliasargs(self.fn) + args
220 if cmd not in commands.norepo.split(' '):
220 if cmd not in commands.norepo.split(' '):
221 self.norepo = False
221 self.norepo = False
222 if self.help.startswith("hg " + cmd):
222 if self.help.startswith("hg " + cmd):
223 # drop prefix in old-style help lines so hg shows the alias
223 # drop prefix in old-style help lines so hg shows the alias
224 self.help = self.help[4 + len(cmd):]
224 self.help = self.help[4 + len(cmd):]
225 self.__doc__ = self.fn.__doc__
225 self.__doc__ = self.fn.__doc__
226
226
227 except error.UnknownCommand:
227 except error.UnknownCommand:
228 def fn(ui, *args):
228 def fn(ui, *args):
229 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
229 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
230 % (self.name, cmd))
230 % (self.name, cmd))
231 try:
231 try:
232 # check if the command is in a disabled extension
232 # check if the command is in a disabled extension
233 commands.help_(ui, cmd, unknowncmd=True)
233 commands.help_(ui, cmd, unknowncmd=True)
234 except error.UnknownCommand:
234 except error.UnknownCommand:
235 pass
235 pass
236 return 1
236 return 1
237 self.fn = fn
237 self.fn = fn
238 self.badalias = True
238 self.badalias = True
239 except error.AmbiguousCommand:
239 except error.AmbiguousCommand:
240 def fn(ui, *args):
240 def fn(ui, *args):
241 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
241 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
242 % (self.name, cmd))
242 % (self.name, cmd))
243 return 1
243 return 1
244 self.fn = fn
244 self.fn = fn
245 self.badalias = True
245 self.badalias = True
246
246
247 def __call__(self, ui, *args, **opts):
247 def __call__(self, ui, *args, **opts):
248 if self.shadows:
248 if self.shadows:
249 ui.debug("alias '%s' shadows command\n" % self.name)
249 ui.debug("alias '%s' shadows command\n" % self.name)
250
250
251 return self.fn(ui, *args, **opts)
251 return self.fn(ui, *args, **opts)
252
252
253 def addaliases(ui, cmdtable):
253 def addaliases(ui, cmdtable):
254 # aliases are processed after extensions have been loaded, so they
254 # aliases are processed after extensions have been loaded, so they
255 # may use extension commands. Aliases can also use other alias definitions,
255 # may use extension commands. Aliases can also use other alias definitions,
256 # but only if they have been defined prior to the current definition.
256 # but only if they have been defined prior to the current definition.
257 for alias, definition in ui.configitems('alias'):
257 for alias, definition in ui.configitems('alias'):
258 aliasdef = cmdalias(alias, definition, cmdtable)
258 aliasdef = cmdalias(alias, definition, cmdtable)
259 cmdtable[alias] = (aliasdef, aliasdef.opts, aliasdef.help)
259 cmdtable[alias] = (aliasdef, aliasdef.opts, aliasdef.help)
260 if aliasdef.norepo:
260 if aliasdef.norepo:
261 commands.norepo += ' %s' % alias
261 commands.norepo += ' %s' % alias
262
262
263 def _parse(ui, args):
263 def _parse(ui, args):
264 options = {}
264 options = {}
265 cmdoptions = {}
265 cmdoptions = {}
266
266
267 try:
267 try:
268 args = fancyopts.fancyopts(args, commands.globalopts, options)
268 args = fancyopts.fancyopts(args, commands.globalopts, options)
269 except fancyopts.getopt.GetoptError, inst:
269 except fancyopts.getopt.GetoptError, inst:
270 raise error.CommandError(None, inst)
270 raise error.CommandError(None, inst)
271
271
272 if args:
272 if args:
273 cmd, args = args[0], args[1:]
273 cmd, args = args[0], args[1:]
274 aliases, entry = cmdutil.findcmd(cmd, commands.table,
274 aliases, entry = cmdutil.findcmd(cmd, commands.table,
275 ui.config("ui", "strict"))
275 ui.config("ui", "strict"))
276 cmd = aliases[0]
276 cmd = aliases[0]
277 args = aliasargs(entry[0]) + args
277 args = aliasargs(entry[0]) + args
278 defaults = ui.config("defaults", cmd)
278 defaults = ui.config("defaults", cmd)
279 if defaults:
279 if defaults:
280 args = map(util.expandpath, shlex.split(defaults)) + args
280 args = map(util.expandpath, shlex.split(defaults)) + args
281 c = list(entry[1])
281 c = list(entry[1])
282 else:
282 else:
283 cmd = None
283 cmd = None
284 c = []
284 c = []
285
285
286 # combine global options into local
286 # combine global options into local
287 for o in commands.globalopts:
287 for o in commands.globalopts:
288 c.append((o[0], o[1], options[o[1]], o[3]))
288 c.append((o[0], o[1], options[o[1]], o[3]))
289
289
290 try:
290 try:
291 args = fancyopts.fancyopts(args, c, cmdoptions, True)
291 args = fancyopts.fancyopts(args, c, cmdoptions, True)
292 except fancyopts.getopt.GetoptError, inst:
292 except fancyopts.getopt.GetoptError, inst:
293 raise error.CommandError(cmd, inst)
293 raise error.CommandError(cmd, inst)
294
294
295 # separate global options back out
295 # separate global options back out
296 for o in commands.globalopts:
296 for o in commands.globalopts:
297 n = o[1]
297 n = o[1]
298 options[n] = cmdoptions[n]
298 options[n] = cmdoptions[n]
299 del cmdoptions[n]
299 del cmdoptions[n]
300
300
301 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
301 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
302
302
303 def _parseconfig(ui, config):
303 def _parseconfig(ui, config):
304 """parse the --config options from the command line"""
304 """parse the --config options from the command line"""
305 for cfg in config:
305 for cfg in config:
306 try:
306 try:
307 name, value = cfg.split('=', 1)
307 name, value = cfg.split('=', 1)
308 section, name = name.split('.', 1)
308 section, name = name.split('.', 1)
309 if not section or not name:
309 if not section or not name:
310 raise IndexError
310 raise IndexError
311 ui.setconfig(section, name, value)
311 ui.setconfig(section, name, value)
312 except (IndexError, ValueError):
312 except (IndexError, ValueError):
313 raise util.Abort(_('malformed --config option: %r '
313 raise util.Abort(_('malformed --config option: %r '
314 '(use --config section.name=value)') % cfg)
314 '(use --config section.name=value)') % cfg)
315
315
316 def _earlygetopt(aliases, args):
316 def _earlygetopt(aliases, args):
317 """Return list of values for an option (or aliases).
317 """Return list of values for an option (or aliases).
318
318
319 The values are listed in the order they appear in args.
319 The values are listed in the order they appear in args.
320 The options and values are removed from args.
320 The options and values are removed from args.
321 """
321 """
322 try:
322 try:
323 argcount = args.index("--")
323 argcount = args.index("--")
324 except ValueError:
324 except ValueError:
325 argcount = len(args)
325 argcount = len(args)
326 shortopts = [opt for opt in aliases if len(opt) == 2]
326 shortopts = [opt for opt in aliases if len(opt) == 2]
327 values = []
327 values = []
328 pos = 0
328 pos = 0
329 while pos < argcount:
329 while pos < argcount:
330 if args[pos] in aliases:
330 if args[pos] in aliases:
331 if pos + 1 >= argcount:
331 if pos + 1 >= argcount:
332 # ignore and let getopt report an error if there is no value
332 # ignore and let getopt report an error if there is no value
333 break
333 break
334 del args[pos]
334 del args[pos]
335 values.append(args.pop(pos))
335 values.append(args.pop(pos))
336 argcount -= 2
336 argcount -= 2
337 elif args[pos][:2] in shortopts:
337 elif args[pos][:2] in shortopts:
338 # short option can have no following space, e.g. hg log -Rfoo
338 # short option can have no following space, e.g. hg log -Rfoo
339 values.append(args.pop(pos)[2:])
339 values.append(args.pop(pos)[2:])
340 argcount -= 1
340 argcount -= 1
341 else:
341 else:
342 pos += 1
342 pos += 1
343 return values
343 return values
344
344
345 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
345 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
346 # run pre-hook, and abort if it fails
346 # run pre-hook, and abort if it fails
347 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
347 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
348 pats=cmdpats, opts=cmdoptions)
348 pats=cmdpats, opts=cmdoptions)
349 if ret:
349 if ret:
350 return ret
350 return ret
351 ret = _runcommand(ui, options, cmd, d)
351 ret = _runcommand(ui, options, cmd, d)
352 # run post-hook, passing command result
352 # run post-hook, passing command result
353 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
353 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
354 result=ret, pats=cmdpats, opts=cmdoptions)
354 result=ret, pats=cmdpats, opts=cmdoptions)
355 return ret
355 return ret
356
356
357 _loaded = set()
357 _loaded = set()
358 def _dispatch(ui, args):
358 def _dispatch(ui, args):
359 # read --config before doing anything else
359 # read --config before doing anything else
360 # (e.g. to change trust settings for reading .hg/hgrc)
360 # (e.g. to change trust settings for reading .hg/hgrc)
361 _parseconfig(ui, _earlygetopt(['--config'], args))
361 _parseconfig(ui, _earlygetopt(['--config'], args))
362
362
363 # check for cwd
363 # check for cwd
364 cwd = _earlygetopt(['--cwd'], args)
364 cwd = _earlygetopt(['--cwd'], args)
365 if cwd:
365 if cwd:
366 os.chdir(cwd[-1])
366 os.chdir(cwd[-1])
367
367
368 # read the local repository .hgrc into a local ui object
368 # read the local repository .hgrc into a local ui object
369 path = cmdutil.findrepo(os.getcwd()) or ""
369 path = cmdutil.findrepo(os.getcwd()) or ""
370 if not path:
370 if not path:
371 lui = ui
371 lui = ui
372 else:
372 else:
373 try:
373 try:
374 lui = ui.copy()
374 lui = ui.copy()
375 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
375 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
376 except IOError:
376 except IOError:
377 pass
377 pass
378
378
379 # now we can expand paths, even ones in .hg/hgrc
379 # now we can expand paths, even ones in .hg/hgrc
380 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
380 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
381 if rpath:
381 if rpath:
382 path = lui.expandpath(rpath[-1])
382 path = lui.expandpath(rpath[-1])
383 lui = ui.copy()
383 lui = ui.copy()
384 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
384 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
385
385
386 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
386 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
387 # reposetup. Programs like TortoiseHg will call _dispatch several
387 # reposetup. Programs like TortoiseHg will call _dispatch several
388 # times so we keep track of configured extensions in _loaded.
388 # times so we keep track of configured extensions in _loaded.
389 extensions.loadall(lui)
389 extensions.loadall(lui)
390 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
390 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
391 # Propagate any changes to lui.__class__ by extensions
391 # Propagate any changes to lui.__class__ by extensions
392 ui.__class__ = lui.__class__
392 ui.__class__ = lui.__class__
393
393
394 # (uisetup and extsetup are handled in extensions.loadall)
394 # (uisetup and extsetup are handled in extensions.loadall)
395
395
396 for name, module in exts:
396 for name, module in exts:
397 cmdtable = getattr(module, 'cmdtable', {})
397 cmdtable = getattr(module, 'cmdtable', {})
398 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
398 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
399 if overrides:
399 if overrides:
400 ui.warn(_("extension '%s' overrides commands: %s\n")
400 ui.warn(_("extension '%s' overrides commands: %s\n")
401 % (name, " ".join(overrides)))
401 % (name, " ".join(overrides)))
402 commands.table.update(cmdtable)
402 commands.table.update(cmdtable)
403 _loaded.add(name)
403 _loaded.add(name)
404
404
405 # (reposetup is handled in hg.repository)
405 # (reposetup is handled in hg.repository)
406
406
407 addaliases(lui, commands.table)
407 addaliases(lui, commands.table)
408
408
409 # check for fallback encoding
409 # check for fallback encoding
410 fallback = lui.config('ui', 'fallbackencoding')
410 fallback = lui.config('ui', 'fallbackencoding')
411 if fallback:
411 if fallback:
412 encoding.fallbackencoding = fallback
412 encoding.fallbackencoding = fallback
413
413
414 fullargs = args
414 fullargs = args
415 cmd, func, args, options, cmdoptions = _parse(lui, args)
415 cmd, func, args, options, cmdoptions = _parse(lui, args)
416
416
417 if options["config"]:
417 if options["config"]:
418 raise util.Abort(_("Option --config may not be abbreviated!"))
418 raise util.Abort(_("Option --config may not be abbreviated!"))
419 if options["cwd"]:
419 if options["cwd"]:
420 raise util.Abort(_("Option --cwd may not be abbreviated!"))
420 raise util.Abort(_("Option --cwd may not be abbreviated!"))
421 if options["repository"]:
421 if options["repository"]:
422 raise util.Abort(_(
422 raise util.Abort(_(
423 "Option -R has to be separated from other options (e.g. not -qR) "
423 "Option -R has to be separated from other options (e.g. not -qR) "
424 "and --repository may only be abbreviated as --repo!"))
424 "and --repository may only be abbreviated as --repo!"))
425
425
426 if options["encoding"]:
426 if options["encoding"]:
427 encoding.encoding = options["encoding"]
427 encoding.encoding = options["encoding"]
428 if options["encodingmode"]:
428 if options["encodingmode"]:
429 encoding.encodingmode = options["encodingmode"]
429 encoding.encodingmode = options["encodingmode"]
430 if options["time"]:
430 if options["time"]:
431 def get_times():
431 def get_times():
432 t = os.times()
432 t = os.times()
433 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
433 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
434 t = (t[0], t[1], t[2], t[3], time.clock())
434 t = (t[0], t[1], t[2], t[3], time.clock())
435 return t
435 return t
436 s = get_times()
436 s = get_times()
437 def print_time():
437 def print_time():
438 t = get_times()
438 t = get_times()
439 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
439 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
440 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
440 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
441 atexit.register(print_time)
441 atexit.register(print_time)
442
442
443 if options['verbose'] or options['debug'] or options['quiet']:
443 if options['verbose'] or options['debug'] or options['quiet']:
444 ui.setconfig('ui', 'verbose', str(bool(options['verbose'])))
444 ui.setconfig('ui', 'verbose', str(bool(options['verbose'])))
445 ui.setconfig('ui', 'debug', str(bool(options['debug'])))
445 ui.setconfig('ui', 'debug', str(bool(options['debug'])))
446 ui.setconfig('ui', 'quiet', str(bool(options['quiet'])))
446 ui.setconfig('ui', 'quiet', str(bool(options['quiet'])))
447 if options['traceback']:
447 if options['traceback']:
448 ui.setconfig('ui', 'traceback', 'on')
448 ui.setconfig('ui', 'traceback', 'on')
449 if options['noninteractive']:
449 if options['noninteractive']:
450 ui.setconfig('ui', 'interactive', 'off')
450 ui.setconfig('ui', 'interactive', 'off')
451
451
452 if options['help']:
452 if options['help']:
453 return commands.help_(ui, cmd, options['version'])
453 return commands.help_(ui, cmd, options['version'])
454 elif options['version']:
454 elif options['version']:
455 return commands.version_(ui)
455 return commands.version_(ui)
456 elif not cmd:
456 elif not cmd:
457 return commands.help_(ui, 'shortlist')
457 return commands.help_(ui, 'shortlist')
458
458
459 repo = None
459 repo = None
460 cmdpats = args[:]
460 cmdpats = args[:]
461 if cmd not in commands.norepo.split():
461 if cmd not in commands.norepo.split():
462 try:
462 try:
463 repo = hg.repository(ui, path=path)
463 repo = hg.repository(ui, path=path)
464 ui = repo.ui
464 ui = repo.ui
465 if not repo.local():
465 if not repo.local():
466 raise util.Abort(_("repository '%s' is not local") % path)
466 raise util.Abort(_("repository '%s' is not local") % path)
467 ui.setconfig("bundle", "mainreporoot", repo.root)
467 ui.setconfig("bundle", "mainreporoot", repo.root)
468 except error.RepoError:
468 except error.RepoError:
469 if cmd not in commands.optionalrepo.split():
469 if cmd not in commands.optionalrepo.split():
470 if args and not path: # try to infer -R from command args
470 if args and not path: # try to infer -R from command args
471 repos = map(cmdutil.findrepo, args)
471 repos = map(cmdutil.findrepo, args)
472 guess = repos[0]
472 guess = repos[0]
473 if guess and repos.count(guess) == len(repos):
473 if guess and repos.count(guess) == len(repos):
474 return _dispatch(ui, ['--repository', guess] + fullargs)
474 return _dispatch(ui, ['--repository', guess] + fullargs)
475 if not path:
475 if not path:
476 raise error.RepoError(_("There is no Mercurial repository"
476 raise error.RepoError(_("There is no Mercurial repository"
477 " here (.hg not found)"))
477 " here (.hg not found)"))
478 raise
478 raise
479 args.insert(0, repo)
479 args.insert(0, repo)
480 elif rpath:
480 elif rpath:
481 ui.warn("warning: --repository ignored\n")
481 ui.warn(_("warning: --repository ignored\n"))
482
482
483 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
483 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
484 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
484 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
485 cmdpats, cmdoptions)
485 cmdpats, cmdoptions)
486
486
487 def _runcommand(ui, options, cmd, cmdfunc):
487 def _runcommand(ui, options, cmd, cmdfunc):
488 def checkargs():
488 def checkargs():
489 try:
489 try:
490 return cmdfunc()
490 return cmdfunc()
491 except error.SignatureError:
491 except error.SignatureError:
492 raise error.CommandError(cmd, _("invalid arguments"))
492 raise error.CommandError(cmd, _("invalid arguments"))
493
493
494 if options['profile']:
494 if options['profile']:
495 format = ui.config('profiling', 'format', default='text')
495 format = ui.config('profiling', 'format', default='text')
496
496
497 if not format in ['text', 'kcachegrind']:
497 if not format in ['text', 'kcachegrind']:
498 ui.warn(_("unrecognized profiling format '%s'"
498 ui.warn(_("unrecognized profiling format '%s'"
499 " - Ignored\n") % format)
499 " - Ignored\n") % format)
500 format = 'text'
500 format = 'text'
501
501
502 output = ui.config('profiling', 'output')
502 output = ui.config('profiling', 'output')
503
503
504 if output:
504 if output:
505 path = ui.expandpath(output)
505 path = ui.expandpath(output)
506 ostream = open(path, 'wb')
506 ostream = open(path, 'wb')
507 else:
507 else:
508 ostream = sys.stderr
508 ostream = sys.stderr
509
509
510 try:
510 try:
511 from mercurial import lsprof
511 from mercurial import lsprof
512 except ImportError:
512 except ImportError:
513 raise util.Abort(_(
513 raise util.Abort(_(
514 'lsprof not available - install from '
514 'lsprof not available - install from '
515 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
515 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
516 p = lsprof.Profiler()
516 p = lsprof.Profiler()
517 p.enable(subcalls=True)
517 p.enable(subcalls=True)
518 try:
518 try:
519 return checkargs()
519 return checkargs()
520 finally:
520 finally:
521 p.disable()
521 p.disable()
522
522
523 if format == 'kcachegrind':
523 if format == 'kcachegrind':
524 import lsprofcalltree
524 import lsprofcalltree
525 calltree = lsprofcalltree.KCacheGrind(p)
525 calltree = lsprofcalltree.KCacheGrind(p)
526 calltree.output(ostream)
526 calltree.output(ostream)
527 else:
527 else:
528 # format == 'text'
528 # format == 'text'
529 stats = lsprof.Stats(p.getstats())
529 stats = lsprof.Stats(p.getstats())
530 stats.sort()
530 stats.sort()
531 stats.pprint(top=10, file=ostream, climit=5)
531 stats.pprint(top=10, file=ostream, climit=5)
532
532
533 if output:
533 if output:
534 ostream.close()
534 ostream.close()
535 else:
535 else:
536 return checkargs()
536 return checkargs()
@@ -1,159 +1,160 b''
1 # repair.py - functions for repository repair for mercurial
1 # repair.py - functions for repository repair for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 # Copyright 2007 Matt Mackall
4 # Copyright 2007 Matt Mackall
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 import changegroup
9 import changegroup
10 from node import nullrev, short
10 from node import nullrev, short
11 from i18n import _
11 from i18n import _
12 import os
12 import os
13
13
14 def _bundle(repo, bases, heads, node, suffix, extranodes=None):
14 def _bundle(repo, bases, heads, node, suffix, extranodes=None):
15 """create a bundle with the specified revisions as a backup"""
15 """create a bundle with the specified revisions as a backup"""
16 cg = repo.changegroupsubset(bases, heads, 'strip', extranodes)
16 cg = repo.changegroupsubset(bases, heads, 'strip', extranodes)
17 backupdir = repo.join("strip-backup")
17 backupdir = repo.join("strip-backup")
18 if not os.path.isdir(backupdir):
18 if not os.path.isdir(backupdir):
19 os.mkdir(backupdir)
19 os.mkdir(backupdir)
20 name = os.path.join(backupdir, "%s-%s.hg" % (short(node), suffix))
20 name = os.path.join(backupdir, "%s-%s.hg" % (short(node), suffix))
21 return changegroup.writebundle(cg, name, "HG10BZ")
21 return changegroup.writebundle(cg, name, "HG10BZ")
22
22
23 def _collectfiles(repo, striprev):
23 def _collectfiles(repo, striprev):
24 """find out the filelogs affected by the strip"""
24 """find out the filelogs affected by the strip"""
25 files = set()
25 files = set()
26
26
27 for x in xrange(striprev, len(repo)):
27 for x in xrange(striprev, len(repo)):
28 files.update(repo[x].files())
28 files.update(repo[x].files())
29
29
30 return sorted(files)
30 return sorted(files)
31
31
32 def _collectextranodes(repo, files, link):
32 def _collectextranodes(repo, files, link):
33 """return the nodes that have to be saved before the strip"""
33 """return the nodes that have to be saved before the strip"""
34 def collectone(revlog):
34 def collectone(revlog):
35 extra = []
35 extra = []
36 startrev = count = len(revlog)
36 startrev = count = len(revlog)
37 # find the truncation point of the revlog
37 # find the truncation point of the revlog
38 for i in xrange(count):
38 for i in xrange(count):
39 lrev = revlog.linkrev(i)
39 lrev = revlog.linkrev(i)
40 if lrev >= link:
40 if lrev >= link:
41 startrev = i + 1
41 startrev = i + 1
42 break
42 break
43
43
44 # see if any revision after that point has a linkrev less than link
44 # see if any revision after that point has a linkrev less than link
45 # (we have to manually save these guys)
45 # (we have to manually save these guys)
46 for i in xrange(startrev, count):
46 for i in xrange(startrev, count):
47 node = revlog.node(i)
47 node = revlog.node(i)
48 lrev = revlog.linkrev(i)
48 lrev = revlog.linkrev(i)
49 if lrev < link:
49 if lrev < link:
50 extra.append((node, cl.node(lrev)))
50 extra.append((node, cl.node(lrev)))
51
51
52 return extra
52 return extra
53
53
54 extranodes = {}
54 extranodes = {}
55 cl = repo.changelog
55 cl = repo.changelog
56 extra = collectone(repo.manifest)
56 extra = collectone(repo.manifest)
57 if extra:
57 if extra:
58 extranodes[1] = extra
58 extranodes[1] = extra
59 for fname in files:
59 for fname in files:
60 f = repo.file(fname)
60 f = repo.file(fname)
61 extra = collectone(f)
61 extra = collectone(f)
62 if extra:
62 if extra:
63 extranodes[fname] = extra
63 extranodes[fname] = extra
64
64
65 return extranodes
65 return extranodes
66
66
67 def strip(ui, repo, node, backup="all"):
67 def strip(ui, repo, node, backup="all"):
68 cl = repo.changelog
68 cl = repo.changelog
69 # TODO delete the undo files, and handle undo of merge sets
69 # TODO delete the undo files, and handle undo of merge sets
70 striprev = cl.rev(node)
70 striprev = cl.rev(node)
71
71
72 # Some revisions with rev > striprev may not be descendants of striprev.
72 # Some revisions with rev > striprev may not be descendants of striprev.
73 # We have to find these revisions and put them in a bundle, so that
73 # We have to find these revisions and put them in a bundle, so that
74 # we can restore them after the truncations.
74 # we can restore them after the truncations.
75 # To create the bundle we use repo.changegroupsubset which requires
75 # To create the bundle we use repo.changegroupsubset which requires
76 # the list of heads and bases of the set of interesting revisions.
76 # the list of heads and bases of the set of interesting revisions.
77 # (head = revision in the set that has no descendant in the set;
77 # (head = revision in the set that has no descendant in the set;
78 # base = revision in the set that has no ancestor in the set)
78 # base = revision in the set that has no ancestor in the set)
79 tostrip = set((striprev,))
79 tostrip = set((striprev,))
80 saveheads = set()
80 saveheads = set()
81 savebases = []
81 savebases = []
82 for r in xrange(striprev + 1, len(cl)):
82 for r in xrange(striprev + 1, len(cl)):
83 parents = cl.parentrevs(r)
83 parents = cl.parentrevs(r)
84 if parents[0] in tostrip or parents[1] in tostrip:
84 if parents[0] in tostrip or parents[1] in tostrip:
85 # r is a descendant of striprev
85 # r is a descendant of striprev
86 tostrip.add(r)
86 tostrip.add(r)
87 # if this is a merge and one of the parents does not descend
87 # if this is a merge and one of the parents does not descend
88 # from striprev, mark that parent as a savehead.
88 # from striprev, mark that parent as a savehead.
89 if parents[1] != nullrev:
89 if parents[1] != nullrev:
90 for p in parents:
90 for p in parents:
91 if p not in tostrip and p > striprev:
91 if p not in tostrip and p > striprev:
92 saveheads.add(p)
92 saveheads.add(p)
93 else:
93 else:
94 # if no parents of this revision will be stripped, mark it as
94 # if no parents of this revision will be stripped, mark it as
95 # a savebase
95 # a savebase
96 if parents[0] < striprev and parents[1] < striprev:
96 if parents[0] < striprev and parents[1] < striprev:
97 savebases.append(cl.node(r))
97 savebases.append(cl.node(r))
98
98
99 saveheads.difference_update(parents)
99 saveheads.difference_update(parents)
100 saveheads.add(r)
100 saveheads.add(r)
101
101
102 saveheads = [cl.node(r) for r in saveheads]
102 saveheads = [cl.node(r) for r in saveheads]
103 files = _collectfiles(repo, striprev)
103 files = _collectfiles(repo, striprev)
104
104
105 extranodes = _collectextranodes(repo, files, striprev)
105 extranodes = _collectextranodes(repo, files, striprev)
106
106
107 # create a changegroup for all the branches we need to keep
107 # create a changegroup for all the branches we need to keep
108 backupfile = None
108 backupfile = None
109 if backup == "all":
109 if backup == "all":
110 backupfile = _bundle(repo, [node], cl.heads(), node, 'backup')
110 backupfile = _bundle(repo, [node], cl.heads(), node, 'backup')
111 repo.ui.status(_("saved backup bundle to %s\n") % backupfile)
111 repo.ui.status(_("saved backup bundle to %s\n") % backupfile)
112 if saveheads or extranodes:
112 if saveheads or extranodes:
113 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
113 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
114 extranodes)
114 extranodes)
115
115
116 mfst = repo.manifest
116 mfst = repo.manifest
117
117
118 tr = repo.transaction("strip")
118 tr = repo.transaction("strip")
119 offset = len(tr.entries)
119 offset = len(tr.entries)
120
120
121 try:
121 try:
122 tr.startgroup()
122 tr.startgroup()
123 cl.strip(striprev, tr)
123 cl.strip(striprev, tr)
124 mfst.strip(striprev, tr)
124 mfst.strip(striprev, tr)
125 for fn in files:
125 for fn in files:
126 repo.file(fn).strip(striprev, tr)
126 repo.file(fn).strip(striprev, tr)
127 tr.endgroup()
127 tr.endgroup()
128
128
129 try:
129 try:
130 for i in xrange(offset, len(tr.entries)):
130 for i in xrange(offset, len(tr.entries)):
131 file, troffset, ignore = tr.entries[i]
131 file, troffset, ignore = tr.entries[i]
132 repo.sopener(file, 'a').truncate(troffset)
132 repo.sopener(file, 'a').truncate(troffset)
133 tr.close()
133 tr.close()
134 except:
134 except:
135 tr.abort()
135 tr.abort()
136 raise
136 raise
137
137
138 if saveheads or extranodes:
138 if saveheads or extranodes:
139 ui.note(_("adding branch\n"))
139 ui.note(_("adding branch\n"))
140 f = open(chgrpfile, "rb")
140 f = open(chgrpfile, "rb")
141 gen = changegroup.readbundle(f, chgrpfile)
141 gen = changegroup.readbundle(f, chgrpfile)
142 if not repo.ui.verbose:
142 if not repo.ui.verbose:
143 # silence internal shuffling chatter
143 # silence internal shuffling chatter
144 repo.ui.pushbuffer()
144 repo.ui.pushbuffer()
145 repo.addchangegroup(gen, 'strip', 'bundle:' + chgrpfile, True)
145 repo.addchangegroup(gen, 'strip', 'bundle:' + chgrpfile, True)
146 if not repo.ui.verbose:
146 if not repo.ui.verbose:
147 repo.ui.popbuffer()
147 repo.ui.popbuffer()
148 f.close()
148 f.close()
149 if backup != "strip":
149 if backup != "strip":
150 os.unlink(chgrpfile)
150 os.unlink(chgrpfile)
151 except:
151 except:
152 if backupfile:
152 if backupfile:
153 ui.warn("strip failed, full bundle stored in '%s'\n" % backupfile)
153 ui.warn(_("strip failed, full bundle stored in '%s'\n")
154 % backupfile)
154 elif saveheads:
155 elif saveheads:
155 ui.warn("strip failed, partial bundle stored in '%s'\n"
156 ui.warn(_("strip failed, partial bundle stored in '%s'\n")
156 % chgrpfile)
157 % chgrpfile)
157 raise
158 raise
158
159
159 repo.destroyed()
160 repo.destroyed()
@@ -1,604 +1,604 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, util, error
10 import config, util, error
11
11
12 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True,
12 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True,
13 '0': False, 'no': False, 'false': False, 'off': False}
13 '0': False, 'no': False, 'false': False, 'off': False}
14
14
15 class ui(object):
15 class ui(object):
16 def __init__(self, src=None):
16 def __init__(self, src=None):
17 self._buffers = []
17 self._buffers = []
18 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
18 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
19 self._reportuntrusted = True
19 self._reportuntrusted = True
20 self._ocfg = config.config() # overlay
20 self._ocfg = config.config() # overlay
21 self._tcfg = config.config() # trusted
21 self._tcfg = config.config() # trusted
22 self._ucfg = config.config() # untrusted
22 self._ucfg = config.config() # untrusted
23 self._trustusers = set()
23 self._trustusers = set()
24 self._trustgroups = set()
24 self._trustgroups = set()
25
25
26 if src:
26 if src:
27 self._tcfg = src._tcfg.copy()
27 self._tcfg = src._tcfg.copy()
28 self._ucfg = src._ucfg.copy()
28 self._ucfg = src._ucfg.copy()
29 self._ocfg = src._ocfg.copy()
29 self._ocfg = src._ocfg.copy()
30 self._trustusers = src._trustusers.copy()
30 self._trustusers = src._trustusers.copy()
31 self._trustgroups = src._trustgroups.copy()
31 self._trustgroups = src._trustgroups.copy()
32 self.environ = src.environ
32 self.environ = src.environ
33 self.fixconfig()
33 self.fixconfig()
34 else:
34 else:
35 # shared read-only environment
35 # shared read-only environment
36 self.environ = os.environ
36 self.environ = os.environ
37 # we always trust global config files
37 # we always trust global config files
38 for f in util.rcpath():
38 for f in util.rcpath():
39 self.readconfig(f, trust=True)
39 self.readconfig(f, trust=True)
40
40
41 def copy(self):
41 def copy(self):
42 return self.__class__(self)
42 return self.__class__(self)
43
43
44 def _is_trusted(self, fp, f):
44 def _is_trusted(self, fp, f):
45 st = util.fstat(fp)
45 st = util.fstat(fp)
46 if util.isowner(st):
46 if util.isowner(st):
47 return True
47 return True
48
48
49 tusers, tgroups = self._trustusers, self._trustgroups
49 tusers, tgroups = self._trustusers, self._trustgroups
50 if '*' in tusers or '*' in tgroups:
50 if '*' in tusers or '*' in tgroups:
51 return True
51 return True
52
52
53 user = util.username(st.st_uid)
53 user = util.username(st.st_uid)
54 group = util.groupname(st.st_gid)
54 group = util.groupname(st.st_gid)
55 if user in tusers or group in tgroups or user == util.username():
55 if user in tusers or group in tgroups or user == util.username():
56 return True
56 return True
57
57
58 if self._reportuntrusted:
58 if self._reportuntrusted:
59 self.warn(_('Not trusting file %s from untrusted '
59 self.warn(_('Not trusting file %s from untrusted '
60 'user %s, group %s\n') % (f, user, group))
60 'user %s, group %s\n') % (f, user, group))
61 return False
61 return False
62
62
63 def readconfig(self, filename, root=None, trust=False,
63 def readconfig(self, filename, root=None, trust=False,
64 sections=None, remap=None):
64 sections=None, remap=None):
65 try:
65 try:
66 fp = open(filename)
66 fp = open(filename)
67 except IOError:
67 except IOError:
68 if not sections: # ignore unless we were looking for something
68 if not sections: # ignore unless we were looking for something
69 return
69 return
70 raise
70 raise
71
71
72 cfg = config.config()
72 cfg = config.config()
73 trusted = sections or trust or self._is_trusted(fp, filename)
73 trusted = sections or trust or self._is_trusted(fp, filename)
74
74
75 try:
75 try:
76 cfg.read(filename, fp, sections=sections, remap=remap)
76 cfg.read(filename, fp, sections=sections, remap=remap)
77 except error.ConfigError, inst:
77 except error.ConfigError, inst:
78 if trusted:
78 if trusted:
79 raise
79 raise
80 self.warn(_("Ignored: %s\n") % str(inst))
80 self.warn(_("Ignored: %s\n") % str(inst))
81
81
82 if self.plain():
82 if self.plain():
83 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
83 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
84 'logtemplate', 'style',
84 'logtemplate', 'style',
85 'traceback', 'verbose'):
85 'traceback', 'verbose'):
86 if k in cfg['ui']:
86 if k in cfg['ui']:
87 del cfg['ui'][k]
87 del cfg['ui'][k]
88 for k, v in cfg.items('alias'):
88 for k, v in cfg.items('alias'):
89 del cfg['alias'][k]
89 del cfg['alias'][k]
90 for k, v in cfg.items('defaults'):
90 for k, v in cfg.items('defaults'):
91 del cfg['defaults'][k]
91 del cfg['defaults'][k]
92
92
93 if trusted:
93 if trusted:
94 self._tcfg.update(cfg)
94 self._tcfg.update(cfg)
95 self._tcfg.update(self._ocfg)
95 self._tcfg.update(self._ocfg)
96 self._ucfg.update(cfg)
96 self._ucfg.update(cfg)
97 self._ucfg.update(self._ocfg)
97 self._ucfg.update(self._ocfg)
98
98
99 if root is None:
99 if root is None:
100 root = os.path.expanduser('~')
100 root = os.path.expanduser('~')
101 self.fixconfig(root=root)
101 self.fixconfig(root=root)
102
102
103 def fixconfig(self, root=None):
103 def fixconfig(self, root=None):
104 # translate paths relative to root (or home) into absolute paths
104 # translate paths relative to root (or home) into absolute paths
105 root = root or os.getcwd()
105 root = root or os.getcwd()
106 for c in self._tcfg, self._ucfg, self._ocfg:
106 for c in self._tcfg, self._ucfg, self._ocfg:
107 for n, p in c.items('paths'):
107 for n, p in c.items('paths'):
108 if p and "://" not in p and not os.path.isabs(p):
108 if p and "://" not in p and not os.path.isabs(p):
109 c.set("paths", n, os.path.normpath(os.path.join(root, p)))
109 c.set("paths", n, os.path.normpath(os.path.join(root, p)))
110
110
111 # update ui options
111 # update ui options
112 self.debugflag = self.configbool('ui', 'debug')
112 self.debugflag = self.configbool('ui', 'debug')
113 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
113 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
114 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
114 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
115 if self.verbose and self.quiet:
115 if self.verbose and self.quiet:
116 self.quiet = self.verbose = False
116 self.quiet = self.verbose = False
117 self._reportuntrusted = self.configbool("ui", "report_untrusted", True)
117 self._reportuntrusted = self.configbool("ui", "report_untrusted", True)
118 self.tracebackflag = self.configbool('ui', 'traceback', False)
118 self.tracebackflag = self.configbool('ui', 'traceback', False)
119
119
120 # update trust information
120 # update trust information
121 self._trustusers.update(self.configlist('trusted', 'users'))
121 self._trustusers.update(self.configlist('trusted', 'users'))
122 self._trustgroups.update(self.configlist('trusted', 'groups'))
122 self._trustgroups.update(self.configlist('trusted', 'groups'))
123
123
124 def setconfig(self, section, name, value):
124 def setconfig(self, section, name, value):
125 for cfg in (self._ocfg, self._tcfg, self._ucfg):
125 for cfg in (self._ocfg, self._tcfg, self._ucfg):
126 cfg.set(section, name, value)
126 cfg.set(section, name, value)
127 self.fixconfig()
127 self.fixconfig()
128
128
129 def _data(self, untrusted):
129 def _data(self, untrusted):
130 return untrusted and self._ucfg or self._tcfg
130 return untrusted and self._ucfg or self._tcfg
131
131
132 def configsource(self, section, name, untrusted=False):
132 def configsource(self, section, name, untrusted=False):
133 return self._data(untrusted).source(section, name) or 'none'
133 return self._data(untrusted).source(section, name) or 'none'
134
134
135 def config(self, section, name, default=None, untrusted=False):
135 def config(self, section, name, default=None, untrusted=False):
136 value = self._data(untrusted).get(section, name, default)
136 value = self._data(untrusted).get(section, name, default)
137 if self.debugflag and not untrusted and self._reportuntrusted:
137 if self.debugflag and not untrusted and self._reportuntrusted:
138 uvalue = self._ucfg.get(section, name)
138 uvalue = self._ucfg.get(section, name)
139 if uvalue is not None and uvalue != value:
139 if uvalue is not None and uvalue != value:
140 self.debug(_("ignoring untrusted configuration option "
140 self.debug(_("ignoring untrusted configuration option "
141 "%s.%s = %s\n") % (section, name, uvalue))
141 "%s.%s = %s\n") % (section, name, uvalue))
142 return value
142 return value
143
143
144 def configbool(self, section, name, default=False, untrusted=False):
144 def configbool(self, section, name, default=False, untrusted=False):
145 v = self.config(section, name, None, untrusted)
145 v = self.config(section, name, None, untrusted)
146 if v is None:
146 if v is None:
147 return default
147 return default
148 if isinstance(v, bool):
148 if isinstance(v, bool):
149 return v
149 return v
150 if v.lower() not in _booleans:
150 if v.lower() not in _booleans:
151 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
151 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
152 % (section, name, v))
152 % (section, name, v))
153 return _booleans[v.lower()]
153 return _booleans[v.lower()]
154
154
155 def configlist(self, section, name, default=None, untrusted=False):
155 def configlist(self, section, name, default=None, untrusted=False):
156 """Return a list of comma/space separated strings"""
156 """Return a list of comma/space separated strings"""
157
157
158 def _parse_plain(parts, s, offset):
158 def _parse_plain(parts, s, offset):
159 whitespace = False
159 whitespace = False
160 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
160 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
161 whitespace = True
161 whitespace = True
162 offset += 1
162 offset += 1
163 if offset >= len(s):
163 if offset >= len(s):
164 return None, parts, offset
164 return None, parts, offset
165 if whitespace:
165 if whitespace:
166 parts.append('')
166 parts.append('')
167 if s[offset] == '"' and not parts[-1]:
167 if s[offset] == '"' and not parts[-1]:
168 return _parse_quote, parts, offset + 1
168 return _parse_quote, parts, offset + 1
169 elif s[offset] == '"' and parts[-1][-1] == '\\':
169 elif s[offset] == '"' and parts[-1][-1] == '\\':
170 parts[-1] = parts[-1][:-1] + s[offset]
170 parts[-1] = parts[-1][:-1] + s[offset]
171 return _parse_plain, parts, offset + 1
171 return _parse_plain, parts, offset + 1
172 parts[-1] += s[offset]
172 parts[-1] += s[offset]
173 return _parse_plain, parts, offset + 1
173 return _parse_plain, parts, offset + 1
174
174
175 def _parse_quote(parts, s, offset):
175 def _parse_quote(parts, s, offset):
176 if offset < len(s) and s[offset] == '"': # ""
176 if offset < len(s) and s[offset] == '"': # ""
177 parts.append('')
177 parts.append('')
178 offset += 1
178 offset += 1
179 while offset < len(s) and (s[offset].isspace() or
179 while offset < len(s) and (s[offset].isspace() or
180 s[offset] == ','):
180 s[offset] == ','):
181 offset += 1
181 offset += 1
182 return _parse_plain, parts, offset
182 return _parse_plain, parts, offset
183
183
184 while offset < len(s) and s[offset] != '"':
184 while offset < len(s) and s[offset] != '"':
185 if (s[offset] == '\\' and offset + 1 < len(s)
185 if (s[offset] == '\\' and offset + 1 < len(s)
186 and s[offset + 1] == '"'):
186 and s[offset + 1] == '"'):
187 offset += 1
187 offset += 1
188 parts[-1] += '"'
188 parts[-1] += '"'
189 else:
189 else:
190 parts[-1] += s[offset]
190 parts[-1] += s[offset]
191 offset += 1
191 offset += 1
192
192
193 if offset >= len(s):
193 if offset >= len(s):
194 real_parts = _configlist(parts[-1])
194 real_parts = _configlist(parts[-1])
195 if not real_parts:
195 if not real_parts:
196 parts[-1] = '"'
196 parts[-1] = '"'
197 else:
197 else:
198 real_parts[0] = '"' + real_parts[0]
198 real_parts[0] = '"' + real_parts[0]
199 parts = parts[:-1]
199 parts = parts[:-1]
200 parts.extend(real_parts)
200 parts.extend(real_parts)
201 return None, parts, offset
201 return None, parts, offset
202
202
203 offset += 1
203 offset += 1
204 while offset < len(s) and s[offset] in [' ', ',']:
204 while offset < len(s) and s[offset] in [' ', ',']:
205 offset += 1
205 offset += 1
206
206
207 if offset < len(s):
207 if offset < len(s):
208 if offset + 1 == len(s) and s[offset] == '"':
208 if offset + 1 == len(s) and s[offset] == '"':
209 parts[-1] += '"'
209 parts[-1] += '"'
210 offset += 1
210 offset += 1
211 else:
211 else:
212 parts.append('')
212 parts.append('')
213 else:
213 else:
214 return None, parts, offset
214 return None, parts, offset
215
215
216 return _parse_plain, parts, offset
216 return _parse_plain, parts, offset
217
217
218 def _configlist(s):
218 def _configlist(s):
219 s = s.rstrip(' ,')
219 s = s.rstrip(' ,')
220 if not s:
220 if not s:
221 return None
221 return None
222 parser, parts, offset = _parse_plain, [''], 0
222 parser, parts, offset = _parse_plain, [''], 0
223 while parser:
223 while parser:
224 parser, parts, offset = parser(parts, s, offset)
224 parser, parts, offset = parser(parts, s, offset)
225 return parts
225 return parts
226
226
227 result = self.config(section, name, untrusted=untrusted)
227 result = self.config(section, name, untrusted=untrusted)
228 if result is None:
228 if result is None:
229 result = default or []
229 result = default or []
230 if isinstance(result, basestring):
230 if isinstance(result, basestring):
231 result = _configlist(result.lstrip(' ,\n'))
231 result = _configlist(result.lstrip(' ,\n'))
232 if result is None:
232 if result is None:
233 result = default or []
233 result = default or []
234 return result
234 return result
235
235
236 def has_section(self, section, untrusted=False):
236 def has_section(self, section, untrusted=False):
237 '''tell whether section exists in config.'''
237 '''tell whether section exists in config.'''
238 return section in self._data(untrusted)
238 return section in self._data(untrusted)
239
239
240 def configitems(self, section, untrusted=False):
240 def configitems(self, section, untrusted=False):
241 items = self._data(untrusted).items(section)
241 items = self._data(untrusted).items(section)
242 if self.debugflag and not untrusted and self._reportuntrusted:
242 if self.debugflag and not untrusted and self._reportuntrusted:
243 for k, v in self._ucfg.items(section):
243 for k, v in self._ucfg.items(section):
244 if self._tcfg.get(section, k) != v:
244 if self._tcfg.get(section, k) != v:
245 self.debug(_("ignoring untrusted configuration option "
245 self.debug(_("ignoring untrusted configuration option "
246 "%s.%s = %s\n") % (section, k, v))
246 "%s.%s = %s\n") % (section, k, v))
247 return items
247 return items
248
248
249 def walkconfig(self, untrusted=False):
249 def walkconfig(self, untrusted=False):
250 cfg = self._data(untrusted)
250 cfg = self._data(untrusted)
251 for section in cfg.sections():
251 for section in cfg.sections():
252 for name, value in self.configitems(section, untrusted):
252 for name, value in self.configitems(section, untrusted):
253 yield section, name, str(value).replace('\n', '\\n')
253 yield section, name, str(value).replace('\n', '\\n')
254
254
255 def plain(self):
255 def plain(self):
256 '''is plain mode active?
256 '''is plain mode active?
257
257
258 Plain mode means that all configuration variables which affect the
258 Plain mode means that all configuration variables which affect the
259 behavior and output of Mercurial should be ignored. Additionally, the
259 behavior and output of Mercurial should be ignored. Additionally, the
260 output should be stable, reproducible and suitable for use in scripts or
260 output should be stable, reproducible and suitable for use in scripts or
261 applications.
261 applications.
262
262
263 The only way to trigger plain mode is by setting the `HGPLAIN'
263 The only way to trigger plain mode is by setting the `HGPLAIN'
264 environment variable.
264 environment variable.
265 '''
265 '''
266 return 'HGPLAIN' in os.environ
266 return 'HGPLAIN' in os.environ
267
267
268 def username(self):
268 def username(self):
269 """Return default username to be used in commits.
269 """Return default username to be used in commits.
270
270
271 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
271 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
272 and stop searching if one of these is set.
272 and stop searching if one of these is set.
273 If not found and ui.askusername is True, ask the user, else use
273 If not found and ui.askusername is True, ask the user, else use
274 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
274 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
275 """
275 """
276 user = os.environ.get("HGUSER")
276 user = os.environ.get("HGUSER")
277 if user is None:
277 if user is None:
278 user = self.config("ui", "username")
278 user = self.config("ui", "username")
279 if user is not None:
279 if user is not None:
280 user = os.path.expandvars(user)
280 user = os.path.expandvars(user)
281 if user is None:
281 if user is None:
282 user = os.environ.get("EMAIL")
282 user = os.environ.get("EMAIL")
283 if user is None and self.configbool("ui", "askusername"):
283 if user is None and self.configbool("ui", "askusername"):
284 user = self.prompt(_("enter a commit username:"), default=None)
284 user = self.prompt(_("enter a commit username:"), default=None)
285 if user is None and not self.interactive():
285 if user is None and not self.interactive():
286 try:
286 try:
287 user = '%s@%s' % (util.getuser(), socket.getfqdn())
287 user = '%s@%s' % (util.getuser(), socket.getfqdn())
288 self.warn(_("No username found, using '%s' instead\n") % user)
288 self.warn(_("No username found, using '%s' instead\n") % user)
289 except KeyError:
289 except KeyError:
290 pass
290 pass
291 if not user:
291 if not user:
292 raise util.Abort(_('no username supplied (see "hg help config")'))
292 raise util.Abort(_('no username supplied (see "hg help config")'))
293 if "\n" in user:
293 if "\n" in user:
294 raise util.Abort(_("username %s contains a newline\n") % repr(user))
294 raise util.Abort(_("username %s contains a newline\n") % repr(user))
295 return user
295 return user
296
296
297 def shortuser(self, user):
297 def shortuser(self, user):
298 """Return a short representation of a user name or email address."""
298 """Return a short representation of a user name or email address."""
299 if not self.verbose:
299 if not self.verbose:
300 user = util.shortuser(user)
300 user = util.shortuser(user)
301 return user
301 return user
302
302
303 def _path(self, loc):
303 def _path(self, loc):
304 p = self.config('paths', loc)
304 p = self.config('paths', loc)
305 if p:
305 if p:
306 if '%%' in p:
306 if '%%' in p:
307 self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
307 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n") %
308 (loc, p, self.configsource('paths', loc)))
308 (loc, p, self.configsource('paths', loc)))
309 p = p.replace('%%', '%')
309 p = p.replace('%%', '%')
310 p = util.expandpath(p)
310 p = util.expandpath(p)
311 return p
311 return p
312
312
313 def expandpath(self, loc, default=None):
313 def expandpath(self, loc, default=None):
314 """Return repository location relative to cwd or from [paths]"""
314 """Return repository location relative to cwd or from [paths]"""
315 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
315 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
316 return loc
316 return loc
317
317
318 path = self._path(loc)
318 path = self._path(loc)
319 if not path and default is not None:
319 if not path and default is not None:
320 path = self._path(default)
320 path = self._path(default)
321 return path or loc
321 return path or loc
322
322
323 def pushbuffer(self):
323 def pushbuffer(self):
324 self._buffers.append([])
324 self._buffers.append([])
325
325
326 def popbuffer(self, labeled=False):
326 def popbuffer(self, labeled=False):
327 '''pop the last buffer and return the buffered output
327 '''pop the last buffer and return the buffered output
328
328
329 If labeled is True, any labels associated with buffered
329 If labeled is True, any labels associated with buffered
330 output will be handled. By default, this has no effect
330 output will be handled. By default, this has no effect
331 on the output returned, but extensions and GUI tools may
331 on the output returned, but extensions and GUI tools may
332 handle this argument and returned styled output. If output
332 handle this argument and returned styled output. If output
333 is being buffered so it can be captured and parsed or
333 is being buffered so it can be captured and parsed or
334 processed, labeled should not be set to True.
334 processed, labeled should not be set to True.
335 '''
335 '''
336 return "".join(self._buffers.pop())
336 return "".join(self._buffers.pop())
337
337
338 def write(self, *args, **opts):
338 def write(self, *args, **opts):
339 '''write args to output
339 '''write args to output
340
340
341 By default, this method simply writes to the buffer or stdout,
341 By default, this method simply writes to the buffer or stdout,
342 but extensions or GUI tools may override this method,
342 but extensions or GUI tools may override this method,
343 write_err(), popbuffer(), and label() to style output from
343 write_err(), popbuffer(), and label() to style output from
344 various parts of hg.
344 various parts of hg.
345
345
346 An optional keyword argument, "label", can be passed in.
346 An optional keyword argument, "label", can be passed in.
347 This should be a string containing label names separated by
347 This should be a string containing label names separated by
348 space. Label names take the form of "topic.type". For example,
348 space. Label names take the form of "topic.type". For example,
349 ui.debug() issues a label of "ui.debug".
349 ui.debug() issues a label of "ui.debug".
350
350
351 When labeling output for a specific command, a label of
351 When labeling output for a specific command, a label of
352 "cmdname.type" is recommended. For example, status issues
352 "cmdname.type" is recommended. For example, status issues
353 a label of "status.modified" for modified files.
353 a label of "status.modified" for modified files.
354 '''
354 '''
355 if self._buffers:
355 if self._buffers:
356 self._buffers[-1].extend([str(a) for a in args])
356 self._buffers[-1].extend([str(a) for a in args])
357 else:
357 else:
358 for a in args:
358 for a in args:
359 sys.stdout.write(str(a))
359 sys.stdout.write(str(a))
360
360
361 def write_err(self, *args, **opts):
361 def write_err(self, *args, **opts):
362 try:
362 try:
363 if not getattr(sys.stdout, 'closed', False):
363 if not getattr(sys.stdout, 'closed', False):
364 sys.stdout.flush()
364 sys.stdout.flush()
365 for a in args:
365 for a in args:
366 sys.stderr.write(str(a))
366 sys.stderr.write(str(a))
367 # stderr may be buffered under win32 when redirected to files,
367 # stderr may be buffered under win32 when redirected to files,
368 # including stdout.
368 # including stdout.
369 if not getattr(sys.stderr, 'closed', False):
369 if not getattr(sys.stderr, 'closed', False):
370 sys.stderr.flush()
370 sys.stderr.flush()
371 except IOError, inst:
371 except IOError, inst:
372 if inst.errno not in (errno.EPIPE, errno.EIO):
372 if inst.errno not in (errno.EPIPE, errno.EIO):
373 raise
373 raise
374
374
375 def flush(self):
375 def flush(self):
376 try: sys.stdout.flush()
376 try: sys.stdout.flush()
377 except: pass
377 except: pass
378 try: sys.stderr.flush()
378 try: sys.stderr.flush()
379 except: pass
379 except: pass
380
380
381 def interactive(self):
381 def interactive(self):
382 '''is interactive input allowed?
382 '''is interactive input allowed?
383
383
384 An interactive session is a session where input can be reasonably read
384 An interactive session is a session where input can be reasonably read
385 from `sys.stdin'. If this function returns false, any attempt to read
385 from `sys.stdin'. If this function returns false, any attempt to read
386 from stdin should fail with an error, unless a sensible default has been
386 from stdin should fail with an error, unless a sensible default has been
387 specified.
387 specified.
388
388
389 Interactiveness is triggered by the value of the `ui.interactive'
389 Interactiveness is triggered by the value of the `ui.interactive'
390 configuration variable or - if it is unset - when `sys.stdin' points
390 configuration variable or - if it is unset - when `sys.stdin' points
391 to a terminal device.
391 to a terminal device.
392
392
393 This function refers to input only; for output, see `ui.formatted()'.
393 This function refers to input only; for output, see `ui.formatted()'.
394 '''
394 '''
395 i = self.configbool("ui", "interactive", None)
395 i = self.configbool("ui", "interactive", None)
396 if i is None:
396 if i is None:
397 try:
397 try:
398 return sys.stdin.isatty()
398 return sys.stdin.isatty()
399 except AttributeError:
399 except AttributeError:
400 # some environments replace stdin without implementing isatty
400 # some environments replace stdin without implementing isatty
401 # usually those are non-interactive
401 # usually those are non-interactive
402 return False
402 return False
403
403
404 return i
404 return i
405
405
406 def formatted(self):
406 def formatted(self):
407 '''should formatted output be used?
407 '''should formatted output be used?
408
408
409 It is often desirable to format the output to suite the output medium.
409 It is often desirable to format the output to suite the output medium.
410 Examples of this are truncating long lines or colorizing messages.
410 Examples of this are truncating long lines or colorizing messages.
411 However, this is not often not desirable when piping output into other
411 However, this is not often not desirable when piping output into other
412 utilities, e.g. `grep'.
412 utilities, e.g. `grep'.
413
413
414 Formatted output is triggered by the value of the `ui.formatted'
414 Formatted output is triggered by the value of the `ui.formatted'
415 configuration variable or - if it is unset - when `sys.stdout' points
415 configuration variable or - if it is unset - when `sys.stdout' points
416 to a terminal device. Please note that `ui.formatted' should be
416 to a terminal device. Please note that `ui.formatted' should be
417 considered an implementation detail; it is not intended for use outside
417 considered an implementation detail; it is not intended for use outside
418 Mercurial or its extensions.
418 Mercurial or its extensions.
419
419
420 This function refers to output only; for input, see `ui.interactive()'.
420 This function refers to output only; for input, see `ui.interactive()'.
421 This function always returns false when in plain mode, see `ui.plain()'.
421 This function always returns false when in plain mode, see `ui.plain()'.
422 '''
422 '''
423 if self.plain():
423 if self.plain():
424 return False
424 return False
425
425
426 i = self.configbool("ui", "formatted", None)
426 i = self.configbool("ui", "formatted", None)
427 if i is None:
427 if i is None:
428 try:
428 try:
429 return sys.stdout.isatty()
429 return sys.stdout.isatty()
430 except AttributeError:
430 except AttributeError:
431 # some environments replace stdout without implementing isatty
431 # some environments replace stdout without implementing isatty
432 # usually those are non-interactive
432 # usually those are non-interactive
433 return False
433 return False
434
434
435 return i
435 return i
436
436
437 def _readline(self, prompt=''):
437 def _readline(self, prompt=''):
438 if sys.stdin.isatty():
438 if sys.stdin.isatty():
439 try:
439 try:
440 # magically add command line editing support, where
440 # magically add command line editing support, where
441 # available
441 # available
442 import readline
442 import readline
443 # force demandimport to really load the module
443 # force demandimport to really load the module
444 readline.read_history_file
444 readline.read_history_file
445 # windows sometimes raises something other than ImportError
445 # windows sometimes raises something other than ImportError
446 except Exception:
446 except Exception:
447 pass
447 pass
448 line = raw_input(prompt)
448 line = raw_input(prompt)
449 # When stdin is in binary mode on Windows, it can cause
449 # When stdin is in binary mode on Windows, it can cause
450 # raw_input() to emit an extra trailing carriage return
450 # raw_input() to emit an extra trailing carriage return
451 if os.linesep == '\r\n' and line and line[-1] == '\r':
451 if os.linesep == '\r\n' and line and line[-1] == '\r':
452 line = line[:-1]
452 line = line[:-1]
453 return line
453 return line
454
454
455 def prompt(self, msg, default="y"):
455 def prompt(self, msg, default="y"):
456 """Prompt user with msg, read response.
456 """Prompt user with msg, read response.
457 If ui is not interactive, the default is returned.
457 If ui is not interactive, the default is returned.
458 """
458 """
459 if not self.interactive():
459 if not self.interactive():
460 self.write(msg, ' ', default, "\n")
460 self.write(msg, ' ', default, "\n")
461 return default
461 return default
462 try:
462 try:
463 r = self._readline(msg + ' ')
463 r = self._readline(msg + ' ')
464 if not r:
464 if not r:
465 return default
465 return default
466 return r
466 return r
467 except EOFError:
467 except EOFError:
468 raise util.Abort(_('response expected'))
468 raise util.Abort(_('response expected'))
469
469
470 def promptchoice(self, msg, choices, default=0):
470 def promptchoice(self, msg, choices, default=0):
471 """Prompt user with msg, read response, and ensure it matches
471 """Prompt user with msg, read response, and ensure it matches
472 one of the provided choices. The index of the choice is returned.
472 one of the provided choices. The index of the choice is returned.
473 choices is a sequence of acceptable responses with the format:
473 choices is a sequence of acceptable responses with the format:
474 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
474 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
475 If ui is not interactive, the default is returned.
475 If ui is not interactive, the default is returned.
476 """
476 """
477 resps = [s[s.index('&')+1].lower() for s in choices]
477 resps = [s[s.index('&')+1].lower() for s in choices]
478 while True:
478 while True:
479 r = self.prompt(msg, resps[default])
479 r = self.prompt(msg, resps[default])
480 if r.lower() in resps:
480 if r.lower() in resps:
481 return resps.index(r.lower())
481 return resps.index(r.lower())
482 self.write(_("unrecognized response\n"))
482 self.write(_("unrecognized response\n"))
483
483
484 def getpass(self, prompt=None, default=None):
484 def getpass(self, prompt=None, default=None):
485 if not self.interactive():
485 if not self.interactive():
486 return default
486 return default
487 try:
487 try:
488 return getpass.getpass(prompt or _('password: '))
488 return getpass.getpass(prompt or _('password: '))
489 except EOFError:
489 except EOFError:
490 raise util.Abort(_('response expected'))
490 raise util.Abort(_('response expected'))
491 def status(self, *msg, **opts):
491 def status(self, *msg, **opts):
492 '''write status message to output (if ui.quiet is False)
492 '''write status message to output (if ui.quiet is False)
493
493
494 This adds an output label of "ui.status".
494 This adds an output label of "ui.status".
495 '''
495 '''
496 if not self.quiet:
496 if not self.quiet:
497 opts['label'] = opts.get('label', '') + ' ui.status'
497 opts['label'] = opts.get('label', '') + ' ui.status'
498 self.write(*msg, **opts)
498 self.write(*msg, **opts)
499 def warn(self, *msg, **opts):
499 def warn(self, *msg, **opts):
500 '''write warning message to output (stderr)
500 '''write warning message to output (stderr)
501
501
502 This adds an output label of "ui.warning".
502 This adds an output label of "ui.warning".
503 '''
503 '''
504 opts['label'] = opts.get('label', '') + ' ui.warning'
504 opts['label'] = opts.get('label', '') + ' ui.warning'
505 self.write_err(*msg, **opts)
505 self.write_err(*msg, **opts)
506 def note(self, *msg, **opts):
506 def note(self, *msg, **opts):
507 '''write note to output (if ui.verbose is True)
507 '''write note to output (if ui.verbose is True)
508
508
509 This adds an output label of "ui.note".
509 This adds an output label of "ui.note".
510 '''
510 '''
511 if self.verbose:
511 if self.verbose:
512 opts['label'] = opts.get('label', '') + ' ui.note'
512 opts['label'] = opts.get('label', '') + ' ui.note'
513 self.write(*msg, **opts)
513 self.write(*msg, **opts)
514 def debug(self, *msg, **opts):
514 def debug(self, *msg, **opts):
515 '''write debug message to output (if ui.debugflag is True)
515 '''write debug message to output (if ui.debugflag is True)
516
516
517 This adds an output label of "ui.debug".
517 This adds an output label of "ui.debug".
518 '''
518 '''
519 if self.debugflag:
519 if self.debugflag:
520 opts['label'] = opts.get('label', '') + ' ui.debug'
520 opts['label'] = opts.get('label', '') + ' ui.debug'
521 self.write(*msg, **opts)
521 self.write(*msg, **opts)
522 def edit(self, text, user):
522 def edit(self, text, user):
523 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
523 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
524 text=True)
524 text=True)
525 try:
525 try:
526 f = os.fdopen(fd, "w")
526 f = os.fdopen(fd, "w")
527 f.write(text)
527 f.write(text)
528 f.close()
528 f.close()
529
529
530 editor = self.geteditor()
530 editor = self.geteditor()
531
531
532 util.system("%s \"%s\"" % (editor, name),
532 util.system("%s \"%s\"" % (editor, name),
533 environ={'HGUSER': user},
533 environ={'HGUSER': user},
534 onerr=util.Abort, errprefix=_("edit failed"))
534 onerr=util.Abort, errprefix=_("edit failed"))
535
535
536 f = open(name)
536 f = open(name)
537 t = f.read()
537 t = f.read()
538 f.close()
538 f.close()
539 finally:
539 finally:
540 os.unlink(name)
540 os.unlink(name)
541
541
542 return t
542 return t
543
543
544 def traceback(self, exc=None):
544 def traceback(self, exc=None):
545 '''print exception traceback if traceback printing enabled.
545 '''print exception traceback if traceback printing enabled.
546 only to call in exception handler. returns true if traceback
546 only to call in exception handler. returns true if traceback
547 printed.'''
547 printed.'''
548 if self.tracebackflag:
548 if self.tracebackflag:
549 if exc:
549 if exc:
550 traceback.print_exception(exc[0], exc[1], exc[2])
550 traceback.print_exception(exc[0], exc[1], exc[2])
551 else:
551 else:
552 traceback.print_exc()
552 traceback.print_exc()
553 return self.tracebackflag
553 return self.tracebackflag
554
554
555 def geteditor(self):
555 def geteditor(self):
556 '''return editor to use'''
556 '''return editor to use'''
557 return (os.environ.get("HGEDITOR") or
557 return (os.environ.get("HGEDITOR") or
558 self.config("ui", "editor") or
558 self.config("ui", "editor") or
559 os.environ.get("VISUAL") or
559 os.environ.get("VISUAL") or
560 os.environ.get("EDITOR", "vi"))
560 os.environ.get("EDITOR", "vi"))
561
561
562 def progress(self, topic, pos, item="", unit="", total=None):
562 def progress(self, topic, pos, item="", unit="", total=None):
563 '''show a progress message
563 '''show a progress message
564
564
565 With stock hg, this is simply a debug message that is hidden
565 With stock hg, this is simply a debug message that is hidden
566 by default, but with extensions or GUI tools it may be
566 by default, but with extensions or GUI tools it may be
567 visible. 'topic' is the current operation, 'item' is a
567 visible. 'topic' is the current operation, 'item' is a
568 non-numeric marker of the current position (ie the currently
568 non-numeric marker of the current position (ie the currently
569 in-process file), 'pos' is the current numeric position (ie
569 in-process file), 'pos' is the current numeric position (ie
570 revision, bytes, etc.), unit is a corresponding unit label,
570 revision, bytes, etc.), unit is a corresponding unit label,
571 and total is the highest expected pos.
571 and total is the highest expected pos.
572
572
573 Multiple nested topics may be active at a time.
573 Multiple nested topics may be active at a time.
574
574
575 All topics should be marked closed by setting pos to None at
575 All topics should be marked closed by setting pos to None at
576 termination.
576 termination.
577 '''
577 '''
578
578
579 if pos == None or not self.debugflag:
579 if pos == None or not self.debugflag:
580 return
580 return
581
581
582 if unit:
582 if unit:
583 unit = ' ' + unit
583 unit = ' ' + unit
584 if item:
584 if item:
585 item = ' ' + item
585 item = ' ' + item
586
586
587 if total:
587 if total:
588 pct = 100.0 * pos / total
588 pct = 100.0 * pos / total
589 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
589 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
590 % (topic, item, pos, total, unit, pct))
590 % (topic, item, pos, total, unit, pct))
591 else:
591 else:
592 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
592 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
593
593
594 def label(self, msg, label):
594 def label(self, msg, label):
595 '''style msg based on supplied label
595 '''style msg based on supplied label
596
596
597 Like ui.write(), this just returns msg unchanged, but extensions
597 Like ui.write(), this just returns msg unchanged, but extensions
598 and GUI tools can override it to allow styling output without
598 and GUI tools can override it to allow styling output without
599 writing it.
599 writing it.
600
600
601 ui.write(s, 'label') is equivalent to
601 ui.write(s, 'label') is equivalent to
602 ui.write(ui.label(s, 'label')).
602 ui.write(ui.label(s, 'label')).
603 '''
603 '''
604 return msg
604 return msg
General Comments 0
You need to be logged in to leave comments. Login now