##// END OF EJS Templates
dispatch: wrap dispatch related information in a request class...
Idan Kamara -
r14438:08bfec2e default
parent child Browse files
Show More
@@ -1,667 +1,673
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):
15 def __init__(self, args):
16 self.args = args
17
14 def run():
18 def run():
15 "run the command in sys.argv"
19 "run the command in sys.argv"
16 sys.exit(dispatch(sys.argv[1:]))
20 sys.exit(dispatch(request(sys.argv[1:])))
17
21
18 def dispatch(args):
22 def dispatch(req):
19 "run the command specified in args"
23 "run the command specified in req.args"
20 try:
24 try:
21 u = uimod.ui()
25 u = uimod.ui()
22 if '--traceback' in args:
26 if '--traceback' in req.args:
23 u.setconfig('ui', 'traceback', 'on')
27 u.setconfig('ui', 'traceback', 'on')
24 except util.Abort, inst:
28 except util.Abort, inst:
25 sys.stderr.write(_("abort: %s\n") % inst)
29 sys.stderr.write(_("abort: %s\n") % inst)
26 if inst.hint:
30 if inst.hint:
27 sys.stderr.write(_("(%s)\n") % inst.hint)
31 sys.stderr.write(_("(%s)\n") % inst.hint)
28 return -1
32 return -1
29 except error.ParseError, inst:
33 except error.ParseError, inst:
30 if len(inst.args) > 1:
34 if len(inst.args) > 1:
31 sys.stderr.write(_("hg: parse error at %s: %s\n") %
35 sys.stderr.write(_("hg: parse error at %s: %s\n") %
32 (inst.args[1], inst.args[0]))
36 (inst.args[1], inst.args[0]))
33 else:
37 else:
34 sys.stderr.write(_("hg: parse error: %s\n") % inst.args[0])
38 sys.stderr.write(_("hg: parse error: %s\n") % inst.args[0])
35 return -1
39 return -1
36 return _runcatch(u, args)
40 return _runcatch(u, req)
37
41
38 def _runcatch(ui, args):
42 def _runcatch(ui, req):
39 def catchterm(*args):
43 def catchterm(*args):
40 raise error.SignalInterrupt
44 raise error.SignalInterrupt
41
45
42 try:
46 try:
43 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
47 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
44 num = getattr(signal, name, None)
48 num = getattr(signal, name, None)
45 if num:
49 if num:
46 signal.signal(num, catchterm)
50 signal.signal(num, catchterm)
47 except ValueError:
51 except ValueError:
48 pass # happens if called in a thread
52 pass # happens if called in a thread
49
53
50 try:
54 try:
51 try:
55 try:
52 # enter the debugger before command execution
56 # enter the debugger before command execution
53 if '--debugger' in args:
57 if '--debugger' in req.args:
54 ui.warn(_("entering debugger - "
58 ui.warn(_("entering debugger - "
55 "type c to continue starting hg or h for help\n"))
59 "type c to continue starting hg or h for help\n"))
56 pdb.set_trace()
60 pdb.set_trace()
57 try:
61 try:
58 return _dispatch(ui, args)
62 return _dispatch(ui, req)
59 finally:
63 finally:
60 ui.flush()
64 ui.flush()
61 except:
65 except:
62 # enter the debugger when we hit an exception
66 # enter the debugger when we hit an exception
63 if '--debugger' in args:
67 if '--debugger' in req.args:
64 traceback.print_exc()
68 traceback.print_exc()
65 pdb.post_mortem(sys.exc_info()[2])
69 pdb.post_mortem(sys.exc_info()[2])
66 ui.traceback()
70 ui.traceback()
67 raise
71 raise
68
72
69 # Global exception handling, alphabetically
73 # Global exception handling, alphabetically
70 # Mercurial-specific first, followed by built-in and library exceptions
74 # Mercurial-specific first, followed by built-in and library exceptions
71 except error.AmbiguousCommand, inst:
75 except error.AmbiguousCommand, inst:
72 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
76 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
73 (inst.args[0], " ".join(inst.args[1])))
77 (inst.args[0], " ".join(inst.args[1])))
74 except error.ParseError, inst:
78 except error.ParseError, inst:
75 if len(inst.args) > 1:
79 if len(inst.args) > 1:
76 ui.warn(_("hg: parse error at %s: %s\n") %
80 ui.warn(_("hg: parse error at %s: %s\n") %
77 (inst.args[1], inst.args[0]))
81 (inst.args[1], inst.args[0]))
78 else:
82 else:
79 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
83 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
80 return -1
84 return -1
81 except error.LockHeld, inst:
85 except error.LockHeld, inst:
82 if inst.errno == errno.ETIMEDOUT:
86 if inst.errno == errno.ETIMEDOUT:
83 reason = _('timed out waiting for lock held by %s') % inst.locker
87 reason = _('timed out waiting for lock held by %s') % inst.locker
84 else:
88 else:
85 reason = _('lock held by %s') % inst.locker
89 reason = _('lock held by %s') % inst.locker
86 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
90 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
87 except error.LockUnavailable, inst:
91 except error.LockUnavailable, inst:
88 ui.warn(_("abort: could not lock %s: %s\n") %
92 ui.warn(_("abort: could not lock %s: %s\n") %
89 (inst.desc or inst.filename, inst.strerror))
93 (inst.desc or inst.filename, inst.strerror))
90 except error.CommandError, inst:
94 except error.CommandError, inst:
91 if inst.args[0]:
95 if inst.args[0]:
92 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
96 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
93 commands.help_(ui, inst.args[0], full=False, command=True)
97 commands.help_(ui, inst.args[0], full=False, command=True)
94 else:
98 else:
95 ui.warn(_("hg: %s\n") % inst.args[1])
99 ui.warn(_("hg: %s\n") % inst.args[1])
96 commands.help_(ui, 'shortlist')
100 commands.help_(ui, 'shortlist')
97 except error.RepoError, inst:
101 except error.RepoError, inst:
98 ui.warn(_("abort: %s!\n") % inst)
102 ui.warn(_("abort: %s!\n") % inst)
99 except error.ResponseError, inst:
103 except error.ResponseError, inst:
100 ui.warn(_("abort: %s") % inst.args[0])
104 ui.warn(_("abort: %s") % inst.args[0])
101 if not isinstance(inst.args[1], basestring):
105 if not isinstance(inst.args[1], basestring):
102 ui.warn(" %r\n" % (inst.args[1],))
106 ui.warn(" %r\n" % (inst.args[1],))
103 elif not inst.args[1]:
107 elif not inst.args[1]:
104 ui.warn(_(" empty string\n"))
108 ui.warn(_(" empty string\n"))
105 else:
109 else:
106 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
110 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
107 except error.RevlogError, inst:
111 except error.RevlogError, inst:
108 ui.warn(_("abort: %s!\n") % inst)
112 ui.warn(_("abort: %s!\n") % inst)
109 except error.SignalInterrupt:
113 except error.SignalInterrupt:
110 ui.warn(_("killed!\n"))
114 ui.warn(_("killed!\n"))
111 except error.UnknownCommand, inst:
115 except error.UnknownCommand, inst:
112 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
116 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
113 try:
117 try:
114 # check if the command is in a disabled extension
118 # check if the command is in a disabled extension
115 # (but don't check for extensions themselves)
119 # (but don't check for extensions themselves)
116 commands.help_(ui, inst.args[0], unknowncmd=True)
120 commands.help_(ui, inst.args[0], unknowncmd=True)
117 except error.UnknownCommand:
121 except error.UnknownCommand:
118 commands.help_(ui, 'shortlist')
122 commands.help_(ui, 'shortlist')
119 except util.Abort, inst:
123 except util.Abort, inst:
120 ui.warn(_("abort: %s\n") % inst)
124 ui.warn(_("abort: %s\n") % inst)
121 if inst.hint:
125 if inst.hint:
122 ui.warn(_("(%s)\n") % inst.hint)
126 ui.warn(_("(%s)\n") % inst.hint)
123 except ImportError, inst:
127 except ImportError, inst:
124 ui.warn(_("abort: %s!\n") % inst)
128 ui.warn(_("abort: %s!\n") % inst)
125 m = str(inst).split()[-1]
129 m = str(inst).split()[-1]
126 if m in "mpatch bdiff".split():
130 if m in "mpatch bdiff".split():
127 ui.warn(_("(did you forget to compile extensions?)\n"))
131 ui.warn(_("(did you forget to compile extensions?)\n"))
128 elif m in "zlib".split():
132 elif m in "zlib".split():
129 ui.warn(_("(is your Python install correct?)\n"))
133 ui.warn(_("(is your Python install correct?)\n"))
130 except IOError, inst:
134 except IOError, inst:
131 if hasattr(inst, "code"):
135 if hasattr(inst, "code"):
132 ui.warn(_("abort: %s\n") % inst)
136 ui.warn(_("abort: %s\n") % inst)
133 elif hasattr(inst, "reason"):
137 elif hasattr(inst, "reason"):
134 try: # usually it is in the form (errno, strerror)
138 try: # usually it is in the form (errno, strerror)
135 reason = inst.reason.args[1]
139 reason = inst.reason.args[1]
136 except (AttributeError, IndexError):
140 except (AttributeError, IndexError):
137 # it might be anything, for example a string
141 # it might be anything, for example a string
138 reason = inst.reason
142 reason = inst.reason
139 ui.warn(_("abort: error: %s\n") % reason)
143 ui.warn(_("abort: error: %s\n") % reason)
140 elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
144 elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
141 if ui.debugflag:
145 if ui.debugflag:
142 ui.warn(_("broken pipe\n"))
146 ui.warn(_("broken pipe\n"))
143 elif getattr(inst, "strerror", None):
147 elif getattr(inst, "strerror", None):
144 if getattr(inst, "filename", None):
148 if getattr(inst, "filename", None):
145 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
149 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
146 else:
150 else:
147 ui.warn(_("abort: %s\n") % inst.strerror)
151 ui.warn(_("abort: %s\n") % inst.strerror)
148 else:
152 else:
149 raise
153 raise
150 except OSError, inst:
154 except OSError, inst:
151 if getattr(inst, "filename", None):
155 if getattr(inst, "filename", None):
152 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
156 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
153 else:
157 else:
154 ui.warn(_("abort: %s\n") % inst.strerror)
158 ui.warn(_("abort: %s\n") % inst.strerror)
155 except KeyboardInterrupt:
159 except KeyboardInterrupt:
156 try:
160 try:
157 ui.warn(_("interrupted!\n"))
161 ui.warn(_("interrupted!\n"))
158 except IOError, inst:
162 except IOError, inst:
159 if inst.errno == errno.EPIPE:
163 if inst.errno == errno.EPIPE:
160 if ui.debugflag:
164 if ui.debugflag:
161 ui.warn(_("\nbroken pipe\n"))
165 ui.warn(_("\nbroken pipe\n"))
162 else:
166 else:
163 raise
167 raise
164 except MemoryError:
168 except MemoryError:
165 ui.warn(_("abort: out of memory\n"))
169 ui.warn(_("abort: out of memory\n"))
166 except SystemExit, inst:
170 except SystemExit, inst:
167 # Commands shouldn't sys.exit directly, but give a return code.
171 # Commands shouldn't sys.exit directly, but give a return code.
168 # Just in case catch this and and pass exit code to caller.
172 # Just in case catch this and and pass exit code to caller.
169 return inst.code
173 return inst.code
170 except socket.error, inst:
174 except socket.error, inst:
171 ui.warn(_("abort: %s\n") % inst.args[-1])
175 ui.warn(_("abort: %s\n") % inst.args[-1])
172 except:
176 except:
173 ui.warn(_("** unknown exception encountered,"
177 ui.warn(_("** unknown exception encountered,"
174 " please report by visiting\n"))
178 " please report by visiting\n"))
175 ui.warn(_("** http://mercurial.selenic.com/wiki/BugTracker\n"))
179 ui.warn(_("** http://mercurial.selenic.com/wiki/BugTracker\n"))
176 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
180 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
177 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
181 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
178 % util.version())
182 % util.version())
179 ui.warn(_("** Extensions loaded: %s\n")
183 ui.warn(_("** Extensions loaded: %s\n")
180 % ", ".join([x[0] for x in extensions.extensions()]))
184 % ", ".join([x[0] for x in extensions.extensions()]))
181 raise
185 raise
182
186
183 return -1
187 return -1
184
188
185 def aliasargs(fn, givenargs):
189 def aliasargs(fn, givenargs):
186 args = getattr(fn, 'args', [])
190 args = getattr(fn, 'args', [])
187 if args and givenargs:
191 if args and givenargs:
188 cmd = ' '.join(map(util.shellquote, args))
192 cmd = ' '.join(map(util.shellquote, args))
189
193
190 nums = []
194 nums = []
191 def replacer(m):
195 def replacer(m):
192 num = int(m.group(1)) - 1
196 num = int(m.group(1)) - 1
193 nums.append(num)
197 nums.append(num)
194 return givenargs[num]
198 return givenargs[num]
195 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
199 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
196 givenargs = [x for i, x in enumerate(givenargs)
200 givenargs = [x for i, x in enumerate(givenargs)
197 if i not in nums]
201 if i not in nums]
198 args = shlex.split(cmd)
202 args = shlex.split(cmd)
199 return args + givenargs
203 return args + givenargs
200
204
201 class cmdalias(object):
205 class cmdalias(object):
202 def __init__(self, name, definition, cmdtable):
206 def __init__(self, name, definition, cmdtable):
203 self.name = self.cmd = name
207 self.name = self.cmd = name
204 self.cmdname = ''
208 self.cmdname = ''
205 self.definition = definition
209 self.definition = definition
206 self.args = []
210 self.args = []
207 self.opts = []
211 self.opts = []
208 self.help = ''
212 self.help = ''
209 self.norepo = True
213 self.norepo = True
210 self.badalias = False
214 self.badalias = False
211
215
212 try:
216 try:
213 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
217 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
214 for alias, e in cmdtable.iteritems():
218 for alias, e in cmdtable.iteritems():
215 if e is entry:
219 if e is entry:
216 self.cmd = alias
220 self.cmd = alias
217 break
221 break
218 self.shadows = True
222 self.shadows = True
219 except error.UnknownCommand:
223 except error.UnknownCommand:
220 self.shadows = False
224 self.shadows = False
221
225
222 if not self.definition:
226 if not self.definition:
223 def fn(ui, *args):
227 def fn(ui, *args):
224 ui.warn(_("no definition for alias '%s'\n") % self.name)
228 ui.warn(_("no definition for alias '%s'\n") % self.name)
225 return 1
229 return 1
226 self.fn = fn
230 self.fn = fn
227 self.badalias = True
231 self.badalias = True
228
232
229 return
233 return
230
234
231 if self.definition.startswith('!'):
235 if self.definition.startswith('!'):
232 self.shell = True
236 self.shell = True
233 def fn(ui, *args):
237 def fn(ui, *args):
234 env = {'HG_ARGS': ' '.join((self.name,) + args)}
238 env = {'HG_ARGS': ' '.join((self.name,) + args)}
235 def _checkvar(m):
239 def _checkvar(m):
236 if m.groups()[0] == '$':
240 if m.groups()[0] == '$':
237 return m.group()
241 return m.group()
238 elif int(m.groups()[0]) <= len(args):
242 elif int(m.groups()[0]) <= len(args):
239 return m.group()
243 return m.group()
240 else:
244 else:
241 ui.debug(_("No argument found for substitution "
245 ui.debug(_("No argument found for substitution "
242 "of %i variable in alias '%s' definition.")
246 "of %i variable in alias '%s' definition.")
243 % (int(m.groups()[0]), self.name))
247 % (int(m.groups()[0]), self.name))
244 return ''
248 return ''
245 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
249 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
246 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
250 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
247 replace['0'] = self.name
251 replace['0'] = self.name
248 replace['@'] = ' '.join(args)
252 replace['@'] = ' '.join(args)
249 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
253 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
250 return util.system(cmd, environ=env)
254 return util.system(cmd, environ=env)
251 self.fn = fn
255 self.fn = fn
252 return
256 return
253
257
254 args = shlex.split(self.definition)
258 args = shlex.split(self.definition)
255 self.cmdname = cmd = args.pop(0)
259 self.cmdname = cmd = args.pop(0)
256 args = map(util.expandpath, args)
260 args = map(util.expandpath, args)
257
261
258 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
262 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
259 if _earlygetopt([invalidarg], args):
263 if _earlygetopt([invalidarg], args):
260 def fn(ui, *args):
264 def fn(ui, *args):
261 ui.warn(_("error in definition for alias '%s': %s may only "
265 ui.warn(_("error in definition for alias '%s': %s may only "
262 "be given on the command line\n")
266 "be given on the command line\n")
263 % (self.name, invalidarg))
267 % (self.name, invalidarg))
264 return 1
268 return 1
265
269
266 self.fn = fn
270 self.fn = fn
267 self.badalias = True
271 self.badalias = True
268 return
272 return
269
273
270 try:
274 try:
271 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
275 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
272 if len(tableentry) > 2:
276 if len(tableentry) > 2:
273 self.fn, self.opts, self.help = tableentry
277 self.fn, self.opts, self.help = tableentry
274 else:
278 else:
275 self.fn, self.opts = tableentry
279 self.fn, self.opts = tableentry
276
280
277 self.args = aliasargs(self.fn, args)
281 self.args = aliasargs(self.fn, args)
278 if cmd not in commands.norepo.split(' '):
282 if cmd not in commands.norepo.split(' '):
279 self.norepo = False
283 self.norepo = False
280 if self.help.startswith("hg " + cmd):
284 if self.help.startswith("hg " + cmd):
281 # drop prefix in old-style help lines so hg shows the alias
285 # drop prefix in old-style help lines so hg shows the alias
282 self.help = self.help[4 + len(cmd):]
286 self.help = self.help[4 + len(cmd):]
283 self.__doc__ = self.fn.__doc__
287 self.__doc__ = self.fn.__doc__
284
288
285 except error.UnknownCommand:
289 except error.UnknownCommand:
286 def fn(ui, *args):
290 def fn(ui, *args):
287 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
291 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
288 % (self.name, cmd))
292 % (self.name, cmd))
289 try:
293 try:
290 # check if the command is in a disabled extension
294 # check if the command is in a disabled extension
291 commands.help_(ui, cmd, unknowncmd=True)
295 commands.help_(ui, cmd, unknowncmd=True)
292 except error.UnknownCommand:
296 except error.UnknownCommand:
293 pass
297 pass
294 return 1
298 return 1
295 self.fn = fn
299 self.fn = fn
296 self.badalias = True
300 self.badalias = True
297 except error.AmbiguousCommand:
301 except error.AmbiguousCommand:
298 def fn(ui, *args):
302 def fn(ui, *args):
299 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
303 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
300 % (self.name, cmd))
304 % (self.name, cmd))
301 return 1
305 return 1
302 self.fn = fn
306 self.fn = fn
303 self.badalias = True
307 self.badalias = True
304
308
305 def __call__(self, ui, *args, **opts):
309 def __call__(self, ui, *args, **opts):
306 if self.shadows:
310 if self.shadows:
307 ui.debug("alias '%s' shadows command '%s'\n" %
311 ui.debug("alias '%s' shadows command '%s'\n" %
308 (self.name, self.cmdname))
312 (self.name, self.cmdname))
309
313
310 if hasattr(self, 'shell'):
314 if hasattr(self, 'shell'):
311 return self.fn(ui, *args, **opts)
315 return self.fn(ui, *args, **opts)
312 else:
316 else:
313 try:
317 try:
314 util.checksignature(self.fn)(ui, *args, **opts)
318 util.checksignature(self.fn)(ui, *args, **opts)
315 except error.SignatureError:
319 except error.SignatureError:
316 args = ' '.join([self.cmdname] + self.args)
320 args = ' '.join([self.cmdname] + self.args)
317 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
321 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
318 raise
322 raise
319
323
320 def addaliases(ui, cmdtable):
324 def addaliases(ui, cmdtable):
321 # aliases are processed after extensions have been loaded, so they
325 # aliases are processed after extensions have been loaded, so they
322 # may use extension commands. Aliases can also use other alias definitions,
326 # may use extension commands. Aliases can also use other alias definitions,
323 # but only if they have been defined prior to the current definition.
327 # but only if they have been defined prior to the current definition.
324 for alias, definition in ui.configitems('alias'):
328 for alias, definition in ui.configitems('alias'):
325 aliasdef = cmdalias(alias, definition, cmdtable)
329 aliasdef = cmdalias(alias, definition, cmdtable)
326 cmdtable[aliasdef.cmd] = (aliasdef, aliasdef.opts, aliasdef.help)
330 cmdtable[aliasdef.cmd] = (aliasdef, aliasdef.opts, aliasdef.help)
327 if aliasdef.norepo:
331 if aliasdef.norepo:
328 commands.norepo += ' %s' % alias
332 commands.norepo += ' %s' % alias
329
333
330 def _parse(ui, args):
334 def _parse(ui, args):
331 options = {}
335 options = {}
332 cmdoptions = {}
336 cmdoptions = {}
333
337
334 try:
338 try:
335 args = fancyopts.fancyopts(args, commands.globalopts, options)
339 args = fancyopts.fancyopts(args, commands.globalopts, options)
336 except fancyopts.getopt.GetoptError, inst:
340 except fancyopts.getopt.GetoptError, inst:
337 raise error.CommandError(None, inst)
341 raise error.CommandError(None, inst)
338
342
339 if args:
343 if args:
340 cmd, args = args[0], args[1:]
344 cmd, args = args[0], args[1:]
341 aliases, entry = cmdutil.findcmd(cmd, commands.table,
345 aliases, entry = cmdutil.findcmd(cmd, commands.table,
342 ui.config("ui", "strict"))
346 ui.config("ui", "strict"))
343 cmd = aliases[0]
347 cmd = aliases[0]
344 args = aliasargs(entry[0], args)
348 args = aliasargs(entry[0], args)
345 defaults = ui.config("defaults", cmd)
349 defaults = ui.config("defaults", cmd)
346 if defaults:
350 if defaults:
347 args = map(util.expandpath, shlex.split(defaults)) + args
351 args = map(util.expandpath, shlex.split(defaults)) + args
348 c = list(entry[1])
352 c = list(entry[1])
349 else:
353 else:
350 cmd = None
354 cmd = None
351 c = []
355 c = []
352
356
353 # combine global options into local
357 # combine global options into local
354 for o in commands.globalopts:
358 for o in commands.globalopts:
355 c.append((o[0], o[1], options[o[1]], o[3]))
359 c.append((o[0], o[1], options[o[1]], o[3]))
356
360
357 try:
361 try:
358 args = fancyopts.fancyopts(args, c, cmdoptions, True)
362 args = fancyopts.fancyopts(args, c, cmdoptions, True)
359 except fancyopts.getopt.GetoptError, inst:
363 except fancyopts.getopt.GetoptError, inst:
360 raise error.CommandError(cmd, inst)
364 raise error.CommandError(cmd, inst)
361
365
362 # separate global options back out
366 # separate global options back out
363 for o in commands.globalopts:
367 for o in commands.globalopts:
364 n = o[1]
368 n = o[1]
365 options[n] = cmdoptions[n]
369 options[n] = cmdoptions[n]
366 del cmdoptions[n]
370 del cmdoptions[n]
367
371
368 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
372 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
369
373
370 def _parseconfig(ui, config):
374 def _parseconfig(ui, config):
371 """parse the --config options from the command line"""
375 """parse the --config options from the command line"""
372 for cfg in config:
376 for cfg in config:
373 try:
377 try:
374 name, value = cfg.split('=', 1)
378 name, value = cfg.split('=', 1)
375 section, name = name.split('.', 1)
379 section, name = name.split('.', 1)
376 if not section or not name:
380 if not section or not name:
377 raise IndexError
381 raise IndexError
378 ui.setconfig(section, name, value)
382 ui.setconfig(section, name, value)
379 except (IndexError, ValueError):
383 except (IndexError, ValueError):
380 raise util.Abort(_('malformed --config option: %r '
384 raise util.Abort(_('malformed --config option: %r '
381 '(use --config section.name=value)') % cfg)
385 '(use --config section.name=value)') % cfg)
382
386
383 def _earlygetopt(aliases, args):
387 def _earlygetopt(aliases, args):
384 """Return list of values for an option (or aliases).
388 """Return list of values for an option (or aliases).
385
389
386 The values are listed in the order they appear in args.
390 The values are listed in the order they appear in args.
387 The options and values are removed from args.
391 The options and values are removed from args.
388 """
392 """
389 try:
393 try:
390 argcount = args.index("--")
394 argcount = args.index("--")
391 except ValueError:
395 except ValueError:
392 argcount = len(args)
396 argcount = len(args)
393 shortopts = [opt for opt in aliases if len(opt) == 2]
397 shortopts = [opt for opt in aliases if len(opt) == 2]
394 values = []
398 values = []
395 pos = 0
399 pos = 0
396 while pos < argcount:
400 while pos < argcount:
397 if args[pos] in aliases:
401 if args[pos] in aliases:
398 if pos + 1 >= argcount:
402 if pos + 1 >= argcount:
399 # ignore and let getopt report an error if there is no value
403 # ignore and let getopt report an error if there is no value
400 break
404 break
401 del args[pos]
405 del args[pos]
402 values.append(args.pop(pos))
406 values.append(args.pop(pos))
403 argcount -= 2
407 argcount -= 2
404 elif args[pos][:2] in shortopts:
408 elif args[pos][:2] in shortopts:
405 # short option can have no following space, e.g. hg log -Rfoo
409 # short option can have no following space, e.g. hg log -Rfoo
406 values.append(args.pop(pos)[2:])
410 values.append(args.pop(pos)[2:])
407 argcount -= 1
411 argcount -= 1
408 else:
412 else:
409 pos += 1
413 pos += 1
410 return values
414 return values
411
415
412 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
416 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
413 # run pre-hook, and abort if it fails
417 # run pre-hook, and abort if it fails
414 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
418 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
415 pats=cmdpats, opts=cmdoptions)
419 pats=cmdpats, opts=cmdoptions)
416 if ret:
420 if ret:
417 return ret
421 return ret
418 ret = _runcommand(ui, options, cmd, d)
422 ret = _runcommand(ui, options, cmd, d)
419 # run post-hook, passing command result
423 # run post-hook, passing command result
420 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
424 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
421 result=ret, pats=cmdpats, opts=cmdoptions)
425 result=ret, pats=cmdpats, opts=cmdoptions)
422 return ret
426 return ret
423
427
424 def _getlocal(ui, rpath):
428 def _getlocal(ui, rpath):
425 """Return (path, local ui object) for the given target path.
429 """Return (path, local ui object) for the given target path.
426
430
427 Takes paths in [cwd]/.hg/hgrc into account."
431 Takes paths in [cwd]/.hg/hgrc into account."
428 """
432 """
429 try:
433 try:
430 wd = os.getcwd()
434 wd = os.getcwd()
431 except OSError, e:
435 except OSError, e:
432 raise util.Abort(_("error getting current working directory: %s") %
436 raise util.Abort(_("error getting current working directory: %s") %
433 e.strerror)
437 e.strerror)
434 path = cmdutil.findrepo(wd) or ""
438 path = cmdutil.findrepo(wd) or ""
435 if not path:
439 if not path:
436 lui = ui
440 lui = ui
437 else:
441 else:
438 lui = ui.copy()
442 lui = ui.copy()
439 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
443 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
440
444
441 if rpath:
445 if rpath:
442 path = lui.expandpath(rpath[-1])
446 path = lui.expandpath(rpath[-1])
443 lui = ui.copy()
447 lui = ui.copy()
444 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
448 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
445
449
446 return path, lui
450 return path, lui
447
451
448 def _checkshellalias(ui, args):
452 def _checkshellalias(ui, args):
449 cwd = os.getcwd()
453 cwd = os.getcwd()
450 norepo = commands.norepo
454 norepo = commands.norepo
451 options = {}
455 options = {}
452
456
453 try:
457 try:
454 args = fancyopts.fancyopts(args, commands.globalopts, options)
458 args = fancyopts.fancyopts(args, commands.globalopts, options)
455 except fancyopts.getopt.GetoptError:
459 except fancyopts.getopt.GetoptError:
456 return
460 return
457
461
458 if not args:
462 if not args:
459 return
463 return
460
464
461 _parseconfig(ui, options['config'])
465 _parseconfig(ui, options['config'])
462 if options['cwd']:
466 if options['cwd']:
463 os.chdir(options['cwd'])
467 os.chdir(options['cwd'])
464
468
465 path, lui = _getlocal(ui, [options['repository']])
469 path, lui = _getlocal(ui, [options['repository']])
466
470
467 cmdtable = commands.table.copy()
471 cmdtable = commands.table.copy()
468 addaliases(lui, cmdtable)
472 addaliases(lui, cmdtable)
469
473
470 cmd = args[0]
474 cmd = args[0]
471 try:
475 try:
472 aliases, entry = cmdutil.findcmd(cmd, cmdtable, lui.config("ui", "strict"))
476 aliases, entry = cmdutil.findcmd(cmd, cmdtable, lui.config("ui", "strict"))
473 except (error.AmbiguousCommand, error.UnknownCommand):
477 except (error.AmbiguousCommand, error.UnknownCommand):
474 commands.norepo = norepo
478 commands.norepo = norepo
475 os.chdir(cwd)
479 os.chdir(cwd)
476 return
480 return
477
481
478 cmd = aliases[0]
482 cmd = aliases[0]
479 fn = entry[0]
483 fn = entry[0]
480
484
481 if cmd and hasattr(fn, 'shell'):
485 if cmd and hasattr(fn, 'shell'):
482 d = lambda: fn(ui, *args[1:])
486 d = lambda: fn(ui, *args[1:])
483 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
487 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
484
488
485 commands.norepo = norepo
489 commands.norepo = norepo
486 os.chdir(cwd)
490 os.chdir(cwd)
487
491
488 _loaded = set()
492 _loaded = set()
489 def _dispatch(ui, args):
493 def _dispatch(ui, req):
494 args = req.args
490 shellaliasfn = _checkshellalias(ui, args)
495 shellaliasfn = _checkshellalias(ui, args)
491 if shellaliasfn:
496 if shellaliasfn:
492 return shellaliasfn()
497 return shellaliasfn()
493
498
494 # read --config before doing anything else
499 # read --config before doing anything else
495 # (e.g. to change trust settings for reading .hg/hgrc)
500 # (e.g. to change trust settings for reading .hg/hgrc)
496 _parseconfig(ui, _earlygetopt(['--config'], args))
501 _parseconfig(ui, _earlygetopt(['--config'], args))
497
502
498 # check for cwd
503 # check for cwd
499 cwd = _earlygetopt(['--cwd'], args)
504 cwd = _earlygetopt(['--cwd'], args)
500 if cwd:
505 if cwd:
501 os.chdir(cwd[-1])
506 os.chdir(cwd[-1])
502
507
503 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
508 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
504 path, lui = _getlocal(ui, rpath)
509 path, lui = _getlocal(ui, rpath)
505
510
506 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
511 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
507 # reposetup. Programs like TortoiseHg will call _dispatch several
512 # reposetup. Programs like TortoiseHg will call _dispatch several
508 # times so we keep track of configured extensions in _loaded.
513 # times so we keep track of configured extensions in _loaded.
509 extensions.loadall(lui)
514 extensions.loadall(lui)
510 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
515 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
511 # Propagate any changes to lui.__class__ by extensions
516 # Propagate any changes to lui.__class__ by extensions
512 ui.__class__ = lui.__class__
517 ui.__class__ = lui.__class__
513
518
514 # (uisetup and extsetup are handled in extensions.loadall)
519 # (uisetup and extsetup are handled in extensions.loadall)
515
520
516 for name, module in exts:
521 for name, module in exts:
517 cmdtable = getattr(module, 'cmdtable', {})
522 cmdtable = getattr(module, 'cmdtable', {})
518 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
523 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
519 if overrides:
524 if overrides:
520 ui.warn(_("extension '%s' overrides commands: %s\n")
525 ui.warn(_("extension '%s' overrides commands: %s\n")
521 % (name, " ".join(overrides)))
526 % (name, " ".join(overrides)))
522 commands.table.update(cmdtable)
527 commands.table.update(cmdtable)
523 _loaded.add(name)
528 _loaded.add(name)
524
529
525 # (reposetup is handled in hg.repository)
530 # (reposetup is handled in hg.repository)
526
531
527 addaliases(lui, commands.table)
532 addaliases(lui, commands.table)
528
533
529 # check for fallback encoding
534 # check for fallback encoding
530 fallback = lui.config('ui', 'fallbackencoding')
535 fallback = lui.config('ui', 'fallbackencoding')
531 if fallback:
536 if fallback:
532 encoding.fallbackencoding = fallback
537 encoding.fallbackencoding = fallback
533
538
534 fullargs = args
539 fullargs = args
535 cmd, func, args, options, cmdoptions = _parse(lui, args)
540 cmd, func, args, options, cmdoptions = _parse(lui, args)
536
541
537 if options["config"]:
542 if options["config"]:
538 raise util.Abort(_("option --config may not be abbreviated!"))
543 raise util.Abort(_("option --config may not be abbreviated!"))
539 if options["cwd"]:
544 if options["cwd"]:
540 raise util.Abort(_("option --cwd may not be abbreviated!"))
545 raise util.Abort(_("option --cwd may not be abbreviated!"))
541 if options["repository"]:
546 if options["repository"]:
542 raise util.Abort(_(
547 raise util.Abort(_(
543 "Option -R has to be separated from other options (e.g. not -qR) "
548 "Option -R has to be separated from other options (e.g. not -qR) "
544 "and --repository may only be abbreviated as --repo!"))
549 "and --repository may only be abbreviated as --repo!"))
545
550
546 if options["encoding"]:
551 if options["encoding"]:
547 encoding.encoding = options["encoding"]
552 encoding.encoding = options["encoding"]
548 if options["encodingmode"]:
553 if options["encodingmode"]:
549 encoding.encodingmode = options["encodingmode"]
554 encoding.encodingmode = options["encodingmode"]
550 if options["time"]:
555 if options["time"]:
551 def get_times():
556 def get_times():
552 t = os.times()
557 t = os.times()
553 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
558 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
554 t = (t[0], t[1], t[2], t[3], time.clock())
559 t = (t[0], t[1], t[2], t[3], time.clock())
555 return t
560 return t
556 s = get_times()
561 s = get_times()
557 def print_time():
562 def print_time():
558 t = get_times()
563 t = get_times()
559 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
564 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
560 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
565 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
561 atexit.register(print_time)
566 atexit.register(print_time)
562
567
563 if options['verbose'] or options['debug'] or options['quiet']:
568 if options['verbose'] or options['debug'] or options['quiet']:
564 ui.setconfig('ui', 'verbose', str(bool(options['verbose'])))
569 ui.setconfig('ui', 'verbose', str(bool(options['verbose'])))
565 ui.setconfig('ui', 'debug', str(bool(options['debug'])))
570 ui.setconfig('ui', 'debug', str(bool(options['debug'])))
566 ui.setconfig('ui', 'quiet', str(bool(options['quiet'])))
571 ui.setconfig('ui', 'quiet', str(bool(options['quiet'])))
567 if options['traceback']:
572 if options['traceback']:
568 ui.setconfig('ui', 'traceback', 'on')
573 ui.setconfig('ui', 'traceback', 'on')
569 if options['noninteractive']:
574 if options['noninteractive']:
570 ui.setconfig('ui', 'interactive', 'off')
575 ui.setconfig('ui', 'interactive', 'off')
571
576
572 if cmdoptions.get('insecure', False):
577 if cmdoptions.get('insecure', False):
573 ui.setconfig('web', 'cacerts', '')
578 ui.setconfig('web', 'cacerts', '')
574
579
575 if options['help']:
580 if options['help']:
576 return commands.help_(ui, cmd, options['version'])
581 return commands.help_(ui, cmd, options['version'])
577 elif options['version']:
582 elif options['version']:
578 return commands.version_(ui)
583 return commands.version_(ui)
579 elif not cmd:
584 elif not cmd:
580 return commands.help_(ui, 'shortlist')
585 return commands.help_(ui, 'shortlist')
581
586
582 repo = None
587 repo = None
583 cmdpats = args[:]
588 cmdpats = args[:]
584 if cmd not in commands.norepo.split():
589 if cmd not in commands.norepo.split():
585 try:
590 try:
586 repo = hg.repository(ui, path=path)
591 repo = hg.repository(ui, path=path)
587 ui = repo.ui
592 ui = repo.ui
588 if not repo.local():
593 if not repo.local():
589 raise util.Abort(_("repository '%s' is not local") % path)
594 raise util.Abort(_("repository '%s' is not local") % path)
590 ui.setconfig("bundle", "mainreporoot", repo.root)
595 ui.setconfig("bundle", "mainreporoot", repo.root)
591 except error.RequirementError:
596 except error.RequirementError:
592 raise
597 raise
593 except error.RepoError:
598 except error.RepoError:
594 if cmd not in commands.optionalrepo.split():
599 if cmd not in commands.optionalrepo.split():
595 if args and not path: # try to infer -R from command args
600 if args and not path: # try to infer -R from command args
596 repos = map(cmdutil.findrepo, args)
601 repos = map(cmdutil.findrepo, args)
597 guess = repos[0]
602 guess = repos[0]
598 if guess and repos.count(guess) == len(repos):
603 if guess and repos.count(guess) == len(repos):
599 return _dispatch(ui, ['--repository', guess] + fullargs)
604 req.args = ['--repository', guess] + fullargs
605 return _dispatch(ui, req)
600 if not path:
606 if not path:
601 raise error.RepoError(_("no repository found in %r"
607 raise error.RepoError(_("no repository found in %r"
602 " (.hg not found)") % os.getcwd())
608 " (.hg not found)") % os.getcwd())
603 raise
609 raise
604 args.insert(0, repo)
610 args.insert(0, repo)
605 elif rpath:
611 elif rpath:
606 ui.warn(_("warning: --repository ignored\n"))
612 ui.warn(_("warning: --repository ignored\n"))
607
613
608 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
614 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
609 ui.log("command", msg + "\n")
615 ui.log("command", msg + "\n")
610 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
616 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
611 try:
617 try:
612 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
618 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
613 cmdpats, cmdoptions)
619 cmdpats, cmdoptions)
614 finally:
620 finally:
615 if repo:
621 if repo:
616 repo.close()
622 repo.close()
617
623
618 def _runcommand(ui, options, cmd, cmdfunc):
624 def _runcommand(ui, options, cmd, cmdfunc):
619 def checkargs():
625 def checkargs():
620 try:
626 try:
621 return cmdfunc()
627 return cmdfunc()
622 except error.SignatureError:
628 except error.SignatureError:
623 raise error.CommandError(cmd, _("invalid arguments"))
629 raise error.CommandError(cmd, _("invalid arguments"))
624
630
625 if options['profile']:
631 if options['profile']:
626 format = ui.config('profiling', 'format', default='text')
632 format = ui.config('profiling', 'format', default='text')
627
633
628 if not format in ['text', 'kcachegrind']:
634 if not format in ['text', 'kcachegrind']:
629 ui.warn(_("unrecognized profiling format '%s'"
635 ui.warn(_("unrecognized profiling format '%s'"
630 " - Ignored\n") % format)
636 " - Ignored\n") % format)
631 format = 'text'
637 format = 'text'
632
638
633 output = ui.config('profiling', 'output')
639 output = ui.config('profiling', 'output')
634
640
635 if output:
641 if output:
636 path = ui.expandpath(output)
642 path = ui.expandpath(output)
637 ostream = open(path, 'wb')
643 ostream = open(path, 'wb')
638 else:
644 else:
639 ostream = sys.stderr
645 ostream = sys.stderr
640
646
641 try:
647 try:
642 from mercurial import lsprof
648 from mercurial import lsprof
643 except ImportError:
649 except ImportError:
644 raise util.Abort(_(
650 raise util.Abort(_(
645 'lsprof not available - install from '
651 'lsprof not available - install from '
646 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
652 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
647 p = lsprof.Profiler()
653 p = lsprof.Profiler()
648 p.enable(subcalls=True)
654 p.enable(subcalls=True)
649 try:
655 try:
650 return checkargs()
656 return checkargs()
651 finally:
657 finally:
652 p.disable()
658 p.disable()
653
659
654 if format == 'kcachegrind':
660 if format == 'kcachegrind':
655 import lsprofcalltree
661 import lsprofcalltree
656 calltree = lsprofcalltree.KCacheGrind(p)
662 calltree = lsprofcalltree.KCacheGrind(p)
657 calltree.output(ostream)
663 calltree.output(ostream)
658 else:
664 else:
659 # format == 'text'
665 # format == 'text'
660 stats = lsprof.Stats(p.getstats())
666 stats = lsprof.Stats(p.getstats())
661 stats.sort()
667 stats.sort()
662 stats.pprint(top=10, file=ostream, climit=5)
668 stats.pprint(top=10, file=ostream, climit=5)
663
669
664 if output:
670 if output:
665 ostream.close()
671 ostream.close()
666 else:
672 else:
667 return checkargs()
673 return checkargs()
@@ -1,32 +1,33
1 import os
1 import os
2 from mercurial import dispatch
2 from mercurial import dispatch
3
3
4 def testdispatch(cmd):
4 def testdispatch(cmd):
5 """Simple wrapper around dispatch.dispatch()
5 """Simple wrapper around dispatch.dispatch()
6
6
7 Prints command and result value, but does not handle quoting.
7 Prints command and result value, but does not handle quoting.
8 """
8 """
9 print "running: %s" % (cmd,)
9 print "running: %s" % (cmd,)
10 result = dispatch.dispatch(cmd.split())
10 req = dispatch.request(cmd.split())
11 result = dispatch.dispatch(req)
11 print "result: %r" % (result,)
12 print "result: %r" % (result,)
12
13
13
14
14 testdispatch("init test1")
15 testdispatch("init test1")
15 os.chdir('test1')
16 os.chdir('test1')
16
17
17 # create file 'foo', add and commit
18 # create file 'foo', add and commit
18 f = open('foo', 'wb')
19 f = open('foo', 'wb')
19 f.write('foo\n')
20 f.write('foo\n')
20 f.close()
21 f.close()
21 testdispatch("add foo")
22 testdispatch("add foo")
22 testdispatch("commit -m commit1 -d 2000-01-01 foo")
23 testdispatch("commit -m commit1 -d 2000-01-01 foo")
23
24
24 # append to file 'foo' and commit
25 # append to file 'foo' and commit
25 f = open('foo', 'ab')
26 f = open('foo', 'ab')
26 f.write('bar\n')
27 f.write('bar\n')
27 f.close()
28 f.close()
28 testdispatch("commit -m commit2 -d 2000-01-02 foo")
29 testdispatch("commit -m commit2 -d 2000-01-02 foo")
29
30
30 # check 88803a69b24 (fancyopts modified command table)
31 # check 88803a69b24 (fancyopts modified command table)
31 testdispatch("log -r 0")
32 testdispatch("log -r 0")
32 testdispatch("log -r tip")
33 testdispatch("log -r tip")
General Comments 0
You need to be logged in to leave comments. Login now