##// END OF EJS Templates
dispatch: only check compatibility against major and minor versions (BC)...
Gregory Szorc -
r23871:b2d8f368 default
parent child Browse files
Show More
@@ -1,941 +1,951 b''
1 # dispatch.py - command dispatching for mercurial
1 # dispatch.py - command dispatching for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
10 import util, commands, hg, fancyopts, extensions, hook, error
10 import util, commands, hg, fancyopts, extensions, hook, error
11 import cmdutil, encoding
11 import cmdutil, encoding
12 import ui as uimod
12 import ui as uimod
13
13
14 class request(object):
14 class request(object):
15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
16 ferr=None):
16 ferr=None):
17 self.args = args
17 self.args = args
18 self.ui = ui
18 self.ui = ui
19 self.repo = repo
19 self.repo = repo
20
20
21 # input/output/error streams
21 # input/output/error streams
22 self.fin = fin
22 self.fin = fin
23 self.fout = fout
23 self.fout = fout
24 self.ferr = ferr
24 self.ferr = ferr
25
25
26 def run():
26 def run():
27 "run the command in sys.argv"
27 "run the command in sys.argv"
28 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
28 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
29
29
30 def dispatch(req):
30 def dispatch(req):
31 "run the command specified in req.args"
31 "run the command specified in req.args"
32 if req.ferr:
32 if req.ferr:
33 ferr = req.ferr
33 ferr = req.ferr
34 elif req.ui:
34 elif req.ui:
35 ferr = req.ui.ferr
35 ferr = req.ui.ferr
36 else:
36 else:
37 ferr = sys.stderr
37 ferr = sys.stderr
38
38
39 try:
39 try:
40 if not req.ui:
40 if not req.ui:
41 req.ui = uimod.ui()
41 req.ui = uimod.ui()
42 if '--traceback' in req.args:
42 if '--traceback' in req.args:
43 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
43 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
44
44
45 # set ui streams from the request
45 # set ui streams from the request
46 if req.fin:
46 if req.fin:
47 req.ui.fin = req.fin
47 req.ui.fin = req.fin
48 if req.fout:
48 if req.fout:
49 req.ui.fout = req.fout
49 req.ui.fout = req.fout
50 if req.ferr:
50 if req.ferr:
51 req.ui.ferr = req.ferr
51 req.ui.ferr = req.ferr
52 except util.Abort, inst:
52 except util.Abort, inst:
53 ferr.write(_("abort: %s\n") % inst)
53 ferr.write(_("abort: %s\n") % inst)
54 if inst.hint:
54 if inst.hint:
55 ferr.write(_("(%s)\n") % inst.hint)
55 ferr.write(_("(%s)\n") % inst.hint)
56 return -1
56 return -1
57 except error.ParseError, inst:
57 except error.ParseError, inst:
58 if len(inst.args) > 1:
58 if len(inst.args) > 1:
59 ferr.write(_("hg: parse error at %s: %s\n") %
59 ferr.write(_("hg: parse error at %s: %s\n") %
60 (inst.args[1], inst.args[0]))
60 (inst.args[1], inst.args[0]))
61 if (inst.args[0][0] == ' '):
61 if (inst.args[0][0] == ' '):
62 ferr.write(_("unexpected leading whitespace\n"))
62 ferr.write(_("unexpected leading whitespace\n"))
63 else:
63 else:
64 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
64 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
65 return -1
65 return -1
66
66
67 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
67 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
68 starttime = time.time()
68 starttime = time.time()
69 ret = None
69 ret = None
70 try:
70 try:
71 ret = _runcatch(req)
71 ret = _runcatch(req)
72 return ret
72 return ret
73 finally:
73 finally:
74 duration = time.time() - starttime
74 duration = time.time() - starttime
75 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
75 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
76 msg, ret or 0, duration)
76 msg, ret or 0, duration)
77
77
78 def _runcatch(req):
78 def _runcatch(req):
79 def catchterm(*args):
79 def catchterm(*args):
80 raise error.SignalInterrupt
80 raise error.SignalInterrupt
81
81
82 ui = req.ui
82 ui = req.ui
83 try:
83 try:
84 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
84 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
85 num = getattr(signal, name, None)
85 num = getattr(signal, name, None)
86 if num:
86 if num:
87 signal.signal(num, catchterm)
87 signal.signal(num, catchterm)
88 except ValueError:
88 except ValueError:
89 pass # happens if called in a thread
89 pass # happens if called in a thread
90
90
91 try:
91 try:
92 try:
92 try:
93 debugger = 'pdb'
93 debugger = 'pdb'
94 debugtrace = {
94 debugtrace = {
95 'pdb' : pdb.set_trace
95 'pdb' : pdb.set_trace
96 }
96 }
97 debugmortem = {
97 debugmortem = {
98 'pdb' : pdb.post_mortem
98 'pdb' : pdb.post_mortem
99 }
99 }
100
100
101 # read --config before doing anything else
101 # read --config before doing anything else
102 # (e.g. to change trust settings for reading .hg/hgrc)
102 # (e.g. to change trust settings for reading .hg/hgrc)
103 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
103 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
104
104
105 if req.repo:
105 if req.repo:
106 # copy configs that were passed on the cmdline (--config) to
106 # copy configs that were passed on the cmdline (--config) to
107 # the repo ui
107 # the repo ui
108 for sec, name, val in cfgs:
108 for sec, name, val in cfgs:
109 req.repo.ui.setconfig(sec, name, val, source='--config')
109 req.repo.ui.setconfig(sec, name, val, source='--config')
110
110
111 # if we are in HGPLAIN mode, then disable custom debugging
111 # if we are in HGPLAIN mode, then disable custom debugging
112 debugger = ui.config("ui", "debugger")
112 debugger = ui.config("ui", "debugger")
113 debugmod = pdb
113 debugmod = pdb
114 if not debugger or ui.plain():
114 if not debugger or ui.plain():
115 debugger = 'pdb'
115 debugger = 'pdb'
116 elif '--debugger' in req.args:
116 elif '--debugger' in req.args:
117 # This import can be slow for fancy debuggers, so only
117 # This import can be slow for fancy debuggers, so only
118 # do it when absolutely necessary, i.e. when actual
118 # do it when absolutely necessary, i.e. when actual
119 # debugging has been requested
119 # debugging has been requested
120 try:
120 try:
121 debugmod = __import__(debugger)
121 debugmod = __import__(debugger)
122 except ImportError:
122 except ImportError:
123 pass # Leave debugmod = pdb
123 pass # Leave debugmod = pdb
124
124
125 debugtrace[debugger] = debugmod.set_trace
125 debugtrace[debugger] = debugmod.set_trace
126 debugmortem[debugger] = debugmod.post_mortem
126 debugmortem[debugger] = debugmod.post_mortem
127
127
128 # enter the debugger before command execution
128 # enter the debugger before command execution
129 if '--debugger' in req.args:
129 if '--debugger' in req.args:
130 ui.warn(_("entering debugger - "
130 ui.warn(_("entering debugger - "
131 "type c to continue starting hg or h for help\n"))
131 "type c to continue starting hg or h for help\n"))
132
132
133 if (debugger != 'pdb' and
133 if (debugger != 'pdb' and
134 debugtrace[debugger] == debugtrace['pdb']):
134 debugtrace[debugger] == debugtrace['pdb']):
135 ui.warn(_("%s debugger specified "
135 ui.warn(_("%s debugger specified "
136 "but its module was not found\n") % debugger)
136 "but its module was not found\n") % debugger)
137
137
138 debugtrace[debugger]()
138 debugtrace[debugger]()
139 try:
139 try:
140 return _dispatch(req)
140 return _dispatch(req)
141 finally:
141 finally:
142 ui.flush()
142 ui.flush()
143 except: # re-raises
143 except: # re-raises
144 # enter the debugger when we hit an exception
144 # enter the debugger when we hit an exception
145 if '--debugger' in req.args:
145 if '--debugger' in req.args:
146 traceback.print_exc()
146 traceback.print_exc()
147 debugmortem[debugger](sys.exc_info()[2])
147 debugmortem[debugger](sys.exc_info()[2])
148 ui.traceback()
148 ui.traceback()
149 raise
149 raise
150
150
151 # Global exception handling, alphabetically
151 # Global exception handling, alphabetically
152 # Mercurial-specific first, followed by built-in and library exceptions
152 # Mercurial-specific first, followed by built-in and library exceptions
153 except error.AmbiguousCommand, inst:
153 except error.AmbiguousCommand, inst:
154 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
154 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
155 (inst.args[0], " ".join(inst.args[1])))
155 (inst.args[0], " ".join(inst.args[1])))
156 except error.ParseError, inst:
156 except error.ParseError, inst:
157 if len(inst.args) > 1:
157 if len(inst.args) > 1:
158 ui.warn(_("hg: parse error at %s: %s\n") %
158 ui.warn(_("hg: parse error at %s: %s\n") %
159 (inst.args[1], inst.args[0]))
159 (inst.args[1], inst.args[0]))
160 if (inst.args[0][0] == ' '):
160 if (inst.args[0][0] == ' '):
161 ui.warn(_("unexpected leading whitespace\n"))
161 ui.warn(_("unexpected leading whitespace\n"))
162 else:
162 else:
163 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
163 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
164 return -1
164 return -1
165 except error.LockHeld, inst:
165 except error.LockHeld, inst:
166 if inst.errno == errno.ETIMEDOUT:
166 if inst.errno == errno.ETIMEDOUT:
167 reason = _('timed out waiting for lock held by %s') % inst.locker
167 reason = _('timed out waiting for lock held by %s') % inst.locker
168 else:
168 else:
169 reason = _('lock held by %s') % inst.locker
169 reason = _('lock held by %s') % inst.locker
170 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
170 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
171 except error.LockUnavailable, inst:
171 except error.LockUnavailable, inst:
172 ui.warn(_("abort: could not lock %s: %s\n") %
172 ui.warn(_("abort: could not lock %s: %s\n") %
173 (inst.desc or inst.filename, inst.strerror))
173 (inst.desc or inst.filename, inst.strerror))
174 except error.CommandError, inst:
174 except error.CommandError, inst:
175 if inst.args[0]:
175 if inst.args[0]:
176 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
176 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
177 commands.help_(ui, inst.args[0], full=False, command=True)
177 commands.help_(ui, inst.args[0], full=False, command=True)
178 else:
178 else:
179 ui.warn(_("hg: %s\n") % inst.args[1])
179 ui.warn(_("hg: %s\n") % inst.args[1])
180 commands.help_(ui, 'shortlist')
180 commands.help_(ui, 'shortlist')
181 except error.OutOfBandError, inst:
181 except error.OutOfBandError, inst:
182 ui.warn(_("abort: remote error:\n"))
182 ui.warn(_("abort: remote error:\n"))
183 ui.warn(''.join(inst.args))
183 ui.warn(''.join(inst.args))
184 except error.RepoError, inst:
184 except error.RepoError, inst:
185 ui.warn(_("abort: %s!\n") % inst)
185 ui.warn(_("abort: %s!\n") % inst)
186 if inst.hint:
186 if inst.hint:
187 ui.warn(_("(%s)\n") % inst.hint)
187 ui.warn(_("(%s)\n") % inst.hint)
188 except error.ResponseError, inst:
188 except error.ResponseError, inst:
189 ui.warn(_("abort: %s") % inst.args[0])
189 ui.warn(_("abort: %s") % inst.args[0])
190 if not isinstance(inst.args[1], basestring):
190 if not isinstance(inst.args[1], basestring):
191 ui.warn(" %r\n" % (inst.args[1],))
191 ui.warn(" %r\n" % (inst.args[1],))
192 elif not inst.args[1]:
192 elif not inst.args[1]:
193 ui.warn(_(" empty string\n"))
193 ui.warn(_(" empty string\n"))
194 else:
194 else:
195 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
195 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
196 except error.CensoredNodeError, inst:
196 except error.CensoredNodeError, inst:
197 ui.warn(_("abort: file censored %s!\n") % inst)
197 ui.warn(_("abort: file censored %s!\n") % inst)
198 except error.RevlogError, inst:
198 except error.RevlogError, inst:
199 ui.warn(_("abort: %s!\n") % inst)
199 ui.warn(_("abort: %s!\n") % inst)
200 except error.SignalInterrupt:
200 except error.SignalInterrupt:
201 ui.warn(_("killed!\n"))
201 ui.warn(_("killed!\n"))
202 except error.UnknownCommand, inst:
202 except error.UnknownCommand, inst:
203 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
203 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
204 try:
204 try:
205 # check if the command is in a disabled extension
205 # check if the command is in a disabled extension
206 # (but don't check for extensions themselves)
206 # (but don't check for extensions themselves)
207 commands.help_(ui, inst.args[0], unknowncmd=True)
207 commands.help_(ui, inst.args[0], unknowncmd=True)
208 except error.UnknownCommand:
208 except error.UnknownCommand:
209 commands.help_(ui, 'shortlist')
209 commands.help_(ui, 'shortlist')
210 except error.InterventionRequired, inst:
210 except error.InterventionRequired, inst:
211 ui.warn("%s\n" % inst)
211 ui.warn("%s\n" % inst)
212 return 1
212 return 1
213 except util.Abort, inst:
213 except util.Abort, inst:
214 ui.warn(_("abort: %s\n") % inst)
214 ui.warn(_("abort: %s\n") % inst)
215 if inst.hint:
215 if inst.hint:
216 ui.warn(_("(%s)\n") % inst.hint)
216 ui.warn(_("(%s)\n") % inst.hint)
217 except ImportError, inst:
217 except ImportError, inst:
218 ui.warn(_("abort: %s!\n") % inst)
218 ui.warn(_("abort: %s!\n") % inst)
219 m = str(inst).split()[-1]
219 m = str(inst).split()[-1]
220 if m in "mpatch bdiff".split():
220 if m in "mpatch bdiff".split():
221 ui.warn(_("(did you forget to compile extensions?)\n"))
221 ui.warn(_("(did you forget to compile extensions?)\n"))
222 elif m in "zlib".split():
222 elif m in "zlib".split():
223 ui.warn(_("(is your Python install correct?)\n"))
223 ui.warn(_("(is your Python install correct?)\n"))
224 except IOError, inst:
224 except IOError, inst:
225 if util.safehasattr(inst, "code"):
225 if util.safehasattr(inst, "code"):
226 ui.warn(_("abort: %s\n") % inst)
226 ui.warn(_("abort: %s\n") % inst)
227 elif util.safehasattr(inst, "reason"):
227 elif util.safehasattr(inst, "reason"):
228 try: # usually it is in the form (errno, strerror)
228 try: # usually it is in the form (errno, strerror)
229 reason = inst.reason.args[1]
229 reason = inst.reason.args[1]
230 except (AttributeError, IndexError):
230 except (AttributeError, IndexError):
231 # it might be anything, for example a string
231 # it might be anything, for example a string
232 reason = inst.reason
232 reason = inst.reason
233 ui.warn(_("abort: error: %s\n") % reason)
233 ui.warn(_("abort: error: %s\n") % reason)
234 elif (util.safehasattr(inst, "args")
234 elif (util.safehasattr(inst, "args")
235 and inst.args and inst.args[0] == errno.EPIPE):
235 and inst.args and inst.args[0] == errno.EPIPE):
236 if ui.debugflag:
236 if ui.debugflag:
237 ui.warn(_("broken pipe\n"))
237 ui.warn(_("broken pipe\n"))
238 elif getattr(inst, "strerror", None):
238 elif getattr(inst, "strerror", None):
239 if getattr(inst, "filename", None):
239 if getattr(inst, "filename", None):
240 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
240 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
241 else:
241 else:
242 ui.warn(_("abort: %s\n") % inst.strerror)
242 ui.warn(_("abort: %s\n") % inst.strerror)
243 else:
243 else:
244 raise
244 raise
245 except OSError, inst:
245 except OSError, inst:
246 if getattr(inst, "filename", None) is not None:
246 if getattr(inst, "filename", None) is not None:
247 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
247 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
248 else:
248 else:
249 ui.warn(_("abort: %s\n") % inst.strerror)
249 ui.warn(_("abort: %s\n") % inst.strerror)
250 except KeyboardInterrupt:
250 except KeyboardInterrupt:
251 try:
251 try:
252 ui.warn(_("interrupted!\n"))
252 ui.warn(_("interrupted!\n"))
253 except IOError, inst:
253 except IOError, inst:
254 if inst.errno == errno.EPIPE:
254 if inst.errno == errno.EPIPE:
255 if ui.debugflag:
255 if ui.debugflag:
256 ui.warn(_("\nbroken pipe\n"))
256 ui.warn(_("\nbroken pipe\n"))
257 else:
257 else:
258 raise
258 raise
259 except MemoryError:
259 except MemoryError:
260 ui.warn(_("abort: out of memory\n"))
260 ui.warn(_("abort: out of memory\n"))
261 except SystemExit, inst:
261 except SystemExit, inst:
262 # Commands shouldn't sys.exit directly, but give a return code.
262 # Commands shouldn't sys.exit directly, but give a return code.
263 # Just in case catch this and and pass exit code to caller.
263 # Just in case catch this and and pass exit code to caller.
264 return inst.code
264 return inst.code
265 except socket.error, inst:
265 except socket.error, inst:
266 ui.warn(_("abort: %s\n") % inst.args[-1])
266 ui.warn(_("abort: %s\n") % inst.args[-1])
267 except: # re-raises
267 except: # re-raises
268 myver = util.version()
268 myver = util.version()
269 # For compatibility checking, we discard the portion of the hg
269 # For compatibility checking, we discard the portion of the hg
270 # version after the + on the assumption that if a "normal
270 # version after the + on the assumption that if a "normal
271 # user" is running a build with a + in it the packager
271 # user" is running a build with a + in it the packager
272 # probably built from fairly close to a tag and anyone with a
272 # probably built from fairly close to a tag and anyone with a
273 # 'make local' copy of hg (where the version number can be out
273 # 'make local' copy of hg (where the version number can be out
274 # of date) will be clueful enough to notice the implausible
274 # of date) will be clueful enough to notice the implausible
275 # version number and try updating.
275 # version number and try updating.
276 compare = myver.split('+')[0]
276 compare = myver.split('+')[0]
277 ct = tuplever(compare)
277 ct = tuplever(compare)
278 worst = None, ct, ''
278 worst = None, ct, ''
279 for name, mod in extensions.extensions():
279 for name, mod in extensions.extensions():
280 testedwith = getattr(mod, 'testedwith', '')
280 testedwith = getattr(mod, 'testedwith', '')
281 report = getattr(mod, 'buglink', _('the extension author.'))
281 report = getattr(mod, 'buglink', _('the extension author.'))
282 if not testedwith.strip():
282 if not testedwith.strip():
283 # We found an untested extension. It's likely the culprit.
283 # We found an untested extension. It's likely the culprit.
284 worst = name, 'unknown', report
284 worst = name, 'unknown', report
285 break
285 break
286 if compare not in testedwith.split() and testedwith != 'internal':
286
287 tested = [tuplever(v) for v in testedwith.split()]
287 # Never blame on extensions bundled with Mercurial.
288 lower = [t for t in tested if t < ct]
288 if testedwith == 'internal':
289 nearest = max(lower or tested)
289 continue
290 if worst[0] is None or nearest < worst[1]:
290
291 worst = name, nearest, report
291 tested = [tuplever(t) for t in testedwith.split()]
292 if ct in tested:
293 continue
294
295 lower = [t for t in tested if t < ct]
296 nearest = max(lower or tested)
297 if worst[0] is None or nearest < worst[1]:
298 worst = name, nearest, report
292 if worst[0] is not None:
299 if worst[0] is not None:
293 name, testedwith, report = worst
300 name, testedwith, report = worst
294 if not isinstance(testedwith, str):
301 if not isinstance(testedwith, str):
295 testedwith = '.'.join([str(c) for c in testedwith])
302 testedwith = '.'.join([str(c) for c in testedwith])
296 warning = (_('** Unknown exception encountered with '
303 warning = (_('** Unknown exception encountered with '
297 'possibly-broken third-party extension %s\n'
304 'possibly-broken third-party extension %s\n'
298 '** which supports versions %s of Mercurial.\n'
305 '** which supports versions %s of Mercurial.\n'
299 '** Please disable %s and try your action again.\n'
306 '** Please disable %s and try your action again.\n'
300 '** If that fixes the bug please report it to %s\n')
307 '** If that fixes the bug please report it to %s\n')
301 % (name, testedwith, name, report))
308 % (name, testedwith, name, report))
302 else:
309 else:
303 warning = (_("** unknown exception encountered, "
310 warning = (_("** unknown exception encountered, "
304 "please report by visiting\n") +
311 "please report by visiting\n") +
305 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
312 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
306 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
313 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
307 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
314 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
308 (_("** Extensions loaded: %s\n") %
315 (_("** Extensions loaded: %s\n") %
309 ", ".join([x[0] for x in extensions.extensions()])))
316 ", ".join([x[0] for x in extensions.extensions()])))
310 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
317 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
311 ui.warn(warning)
318 ui.warn(warning)
312 raise
319 raise
313
320
314 return -1
321 return -1
315
322
316 def tuplever(v):
323 def tuplever(v):
317 try:
324 try:
318 return tuple([int(i) for i in v.split('.')])
325 # Assertion: tuplever is only used for extension compatibility
326 # checking. Otherwise, the discarding of extra version fields is
327 # incorrect.
328 return tuple([int(i) for i in v.split('.')[0:2]])
319 except ValueError:
329 except ValueError:
320 return tuple()
330 return tuple()
321
331
322 def aliasargs(fn, givenargs):
332 def aliasargs(fn, givenargs):
323 args = getattr(fn, 'args', [])
333 args = getattr(fn, 'args', [])
324 if args:
334 if args:
325 cmd = ' '.join(map(util.shellquote, args))
335 cmd = ' '.join(map(util.shellquote, args))
326
336
327 nums = []
337 nums = []
328 def replacer(m):
338 def replacer(m):
329 num = int(m.group(1)) - 1
339 num = int(m.group(1)) - 1
330 nums.append(num)
340 nums.append(num)
331 if num < len(givenargs):
341 if num < len(givenargs):
332 return givenargs[num]
342 return givenargs[num]
333 raise util.Abort(_('too few arguments for command alias'))
343 raise util.Abort(_('too few arguments for command alias'))
334 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
344 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
335 givenargs = [x for i, x in enumerate(givenargs)
345 givenargs = [x for i, x in enumerate(givenargs)
336 if i not in nums]
346 if i not in nums]
337 args = shlex.split(cmd)
347 args = shlex.split(cmd)
338 return args + givenargs
348 return args + givenargs
339
349
340 def aliasinterpolate(name, args, cmd):
350 def aliasinterpolate(name, args, cmd):
341 '''interpolate args into cmd for shell aliases
351 '''interpolate args into cmd for shell aliases
342
352
343 This also handles $0, $@ and "$@".
353 This also handles $0, $@ and "$@".
344 '''
354 '''
345 # util.interpolate can't deal with "$@" (with quotes) because it's only
355 # util.interpolate can't deal with "$@" (with quotes) because it's only
346 # built to match prefix + patterns.
356 # built to match prefix + patterns.
347 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
357 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
348 replacemap['$0'] = name
358 replacemap['$0'] = name
349 replacemap['$$'] = '$'
359 replacemap['$$'] = '$'
350 replacemap['$@'] = ' '.join(args)
360 replacemap['$@'] = ' '.join(args)
351 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
361 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
352 # parameters, separated out into words. Emulate the same behavior here by
362 # parameters, separated out into words. Emulate the same behavior here by
353 # quoting the arguments individually. POSIX shells will then typically
363 # quoting the arguments individually. POSIX shells will then typically
354 # tokenize each argument into exactly one word.
364 # tokenize each argument into exactly one word.
355 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
365 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
356 # escape '\$' for regex
366 # escape '\$' for regex
357 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
367 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
358 r = re.compile(regex)
368 r = re.compile(regex)
359 return r.sub(lambda x: replacemap[x.group()], cmd)
369 return r.sub(lambda x: replacemap[x.group()], cmd)
360
370
361 class cmdalias(object):
371 class cmdalias(object):
362 def __init__(self, name, definition, cmdtable):
372 def __init__(self, name, definition, cmdtable):
363 self.name = self.cmd = name
373 self.name = self.cmd = name
364 self.cmdname = ''
374 self.cmdname = ''
365 self.definition = definition
375 self.definition = definition
366 self.fn = None
376 self.fn = None
367 self.args = []
377 self.args = []
368 self.opts = []
378 self.opts = []
369 self.help = ''
379 self.help = ''
370 self.norepo = True
380 self.norepo = True
371 self.optionalrepo = False
381 self.optionalrepo = False
372 self.badalias = None
382 self.badalias = None
373 self.unknowncmd = False
383 self.unknowncmd = False
374
384
375 try:
385 try:
376 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
386 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
377 for alias, e in cmdtable.iteritems():
387 for alias, e in cmdtable.iteritems():
378 if e is entry:
388 if e is entry:
379 self.cmd = alias
389 self.cmd = alias
380 break
390 break
381 self.shadows = True
391 self.shadows = True
382 except error.UnknownCommand:
392 except error.UnknownCommand:
383 self.shadows = False
393 self.shadows = False
384
394
385 if not self.definition:
395 if not self.definition:
386 self.badalias = _("no definition for alias '%s'") % self.name
396 self.badalias = _("no definition for alias '%s'") % self.name
387 return
397 return
388
398
389 if self.definition.startswith('!'):
399 if self.definition.startswith('!'):
390 self.shell = True
400 self.shell = True
391 def fn(ui, *args):
401 def fn(ui, *args):
392 env = {'HG_ARGS': ' '.join((self.name,) + args)}
402 env = {'HG_ARGS': ' '.join((self.name,) + args)}
393 def _checkvar(m):
403 def _checkvar(m):
394 if m.groups()[0] == '$':
404 if m.groups()[0] == '$':
395 return m.group()
405 return m.group()
396 elif int(m.groups()[0]) <= len(args):
406 elif int(m.groups()[0]) <= len(args):
397 return m.group()
407 return m.group()
398 else:
408 else:
399 ui.debug("No argument found for substitution "
409 ui.debug("No argument found for substitution "
400 "of %i variable in alias '%s' definition."
410 "of %i variable in alias '%s' definition."
401 % (int(m.groups()[0]), self.name))
411 % (int(m.groups()[0]), self.name))
402 return ''
412 return ''
403 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
413 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
404 cmd = aliasinterpolate(self.name, args, cmd)
414 cmd = aliasinterpolate(self.name, args, cmd)
405 return ui.system(cmd, environ=env)
415 return ui.system(cmd, environ=env)
406 self.fn = fn
416 self.fn = fn
407 return
417 return
408
418
409 try:
419 try:
410 args = shlex.split(self.definition)
420 args = shlex.split(self.definition)
411 except ValueError, inst:
421 except ValueError, inst:
412 self.badalias = (_("error in definition for alias '%s': %s")
422 self.badalias = (_("error in definition for alias '%s': %s")
413 % (self.name, inst))
423 % (self.name, inst))
414 return
424 return
415 self.cmdname = cmd = args.pop(0)
425 self.cmdname = cmd = args.pop(0)
416 args = map(util.expandpath, args)
426 args = map(util.expandpath, args)
417
427
418 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
428 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
419 if _earlygetopt([invalidarg], args):
429 if _earlygetopt([invalidarg], args):
420 self.badalias = (_("error in definition for alias '%s': %s may "
430 self.badalias = (_("error in definition for alias '%s': %s may "
421 "only be given on the command line")
431 "only be given on the command line")
422 % (self.name, invalidarg))
432 % (self.name, invalidarg))
423 return
433 return
424
434
425 try:
435 try:
426 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
436 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
427 if len(tableentry) > 2:
437 if len(tableentry) > 2:
428 self.fn, self.opts, self.help = tableentry
438 self.fn, self.opts, self.help = tableentry
429 else:
439 else:
430 self.fn, self.opts = tableentry
440 self.fn, self.opts = tableentry
431
441
432 self.args = aliasargs(self.fn, args)
442 self.args = aliasargs(self.fn, args)
433 if cmd not in commands.norepo.split(' '):
443 if cmd not in commands.norepo.split(' '):
434 self.norepo = False
444 self.norepo = False
435 if cmd in commands.optionalrepo.split(' '):
445 if cmd in commands.optionalrepo.split(' '):
436 self.optionalrepo = True
446 self.optionalrepo = True
437 if self.help.startswith("hg " + cmd):
447 if self.help.startswith("hg " + cmd):
438 # drop prefix in old-style help lines so hg shows the alias
448 # drop prefix in old-style help lines so hg shows the alias
439 self.help = self.help[4 + len(cmd):]
449 self.help = self.help[4 + len(cmd):]
440 self.__doc__ = self.fn.__doc__
450 self.__doc__ = self.fn.__doc__
441
451
442 except error.UnknownCommand:
452 except error.UnknownCommand:
443 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
453 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
444 % (self.name, cmd))
454 % (self.name, cmd))
445 self.unknowncmd = True
455 self.unknowncmd = True
446 except error.AmbiguousCommand:
456 except error.AmbiguousCommand:
447 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
457 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
448 % (self.name, cmd))
458 % (self.name, cmd))
449
459
450 def __call__(self, ui, *args, **opts):
460 def __call__(self, ui, *args, **opts):
451 if self.badalias:
461 if self.badalias:
452 hint = None
462 hint = None
453 if self.unknowncmd:
463 if self.unknowncmd:
454 try:
464 try:
455 # check if the command is in a disabled extension
465 # check if the command is in a disabled extension
456 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
466 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
457 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
467 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
458 except error.UnknownCommand:
468 except error.UnknownCommand:
459 pass
469 pass
460 raise util.Abort(self.badalias, hint=hint)
470 raise util.Abort(self.badalias, hint=hint)
461 if self.shadows:
471 if self.shadows:
462 ui.debug("alias '%s' shadows command '%s'\n" %
472 ui.debug("alias '%s' shadows command '%s'\n" %
463 (self.name, self.cmdname))
473 (self.name, self.cmdname))
464
474
465 if util.safehasattr(self, 'shell'):
475 if util.safehasattr(self, 'shell'):
466 return self.fn(ui, *args, **opts)
476 return self.fn(ui, *args, **opts)
467 else:
477 else:
468 try:
478 try:
469 return util.checksignature(self.fn)(ui, *args, **opts)
479 return util.checksignature(self.fn)(ui, *args, **opts)
470 except error.SignatureError:
480 except error.SignatureError:
471 args = ' '.join([self.cmdname] + self.args)
481 args = ' '.join([self.cmdname] + self.args)
472 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
482 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
473 raise
483 raise
474
484
475 def addaliases(ui, cmdtable):
485 def addaliases(ui, cmdtable):
476 # aliases are processed after extensions have been loaded, so they
486 # aliases are processed after extensions have been loaded, so they
477 # may use extension commands. Aliases can also use other alias definitions,
487 # may use extension commands. Aliases can also use other alias definitions,
478 # but only if they have been defined prior to the current definition.
488 # but only if they have been defined prior to the current definition.
479 for alias, definition in ui.configitems('alias'):
489 for alias, definition in ui.configitems('alias'):
480 aliasdef = cmdalias(alias, definition, cmdtable)
490 aliasdef = cmdalias(alias, definition, cmdtable)
481
491
482 try:
492 try:
483 olddef = cmdtable[aliasdef.cmd][0]
493 olddef = cmdtable[aliasdef.cmd][0]
484 if olddef.definition == aliasdef.definition:
494 if olddef.definition == aliasdef.definition:
485 continue
495 continue
486 except (KeyError, AttributeError):
496 except (KeyError, AttributeError):
487 # definition might not exist or it might not be a cmdalias
497 # definition might not exist or it might not be a cmdalias
488 pass
498 pass
489
499
490 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
500 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
491 if aliasdef.norepo:
501 if aliasdef.norepo:
492 commands.norepo += ' %s' % alias
502 commands.norepo += ' %s' % alias
493 if aliasdef.optionalrepo:
503 if aliasdef.optionalrepo:
494 commands.optionalrepo += ' %s' % alias
504 commands.optionalrepo += ' %s' % alias
495
505
496 def _parse(ui, args):
506 def _parse(ui, args):
497 options = {}
507 options = {}
498 cmdoptions = {}
508 cmdoptions = {}
499
509
500 try:
510 try:
501 args = fancyopts.fancyopts(args, commands.globalopts, options)
511 args = fancyopts.fancyopts(args, commands.globalopts, options)
502 except fancyopts.getopt.GetoptError, inst:
512 except fancyopts.getopt.GetoptError, inst:
503 raise error.CommandError(None, inst)
513 raise error.CommandError(None, inst)
504
514
505 if args:
515 if args:
506 cmd, args = args[0], args[1:]
516 cmd, args = args[0], args[1:]
507 aliases, entry = cmdutil.findcmd(cmd, commands.table,
517 aliases, entry = cmdutil.findcmd(cmd, commands.table,
508 ui.configbool("ui", "strict"))
518 ui.configbool("ui", "strict"))
509 cmd = aliases[0]
519 cmd = aliases[0]
510 args = aliasargs(entry[0], args)
520 args = aliasargs(entry[0], args)
511 defaults = ui.config("defaults", cmd)
521 defaults = ui.config("defaults", cmd)
512 if defaults:
522 if defaults:
513 args = map(util.expandpath, shlex.split(defaults)) + args
523 args = map(util.expandpath, shlex.split(defaults)) + args
514 c = list(entry[1])
524 c = list(entry[1])
515 else:
525 else:
516 cmd = None
526 cmd = None
517 c = []
527 c = []
518
528
519 # combine global options into local
529 # combine global options into local
520 for o in commands.globalopts:
530 for o in commands.globalopts:
521 c.append((o[0], o[1], options[o[1]], o[3]))
531 c.append((o[0], o[1], options[o[1]], o[3]))
522
532
523 try:
533 try:
524 args = fancyopts.fancyopts(args, c, cmdoptions, True)
534 args = fancyopts.fancyopts(args, c, cmdoptions, True)
525 except fancyopts.getopt.GetoptError, inst:
535 except fancyopts.getopt.GetoptError, inst:
526 raise error.CommandError(cmd, inst)
536 raise error.CommandError(cmd, inst)
527
537
528 # separate global options back out
538 # separate global options back out
529 for o in commands.globalopts:
539 for o in commands.globalopts:
530 n = o[1]
540 n = o[1]
531 options[n] = cmdoptions[n]
541 options[n] = cmdoptions[n]
532 del cmdoptions[n]
542 del cmdoptions[n]
533
543
534 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
544 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
535
545
536 def _parseconfig(ui, config):
546 def _parseconfig(ui, config):
537 """parse the --config options from the command line"""
547 """parse the --config options from the command line"""
538 configs = []
548 configs = []
539
549
540 for cfg in config:
550 for cfg in config:
541 try:
551 try:
542 name, value = cfg.split('=', 1)
552 name, value = cfg.split('=', 1)
543 section, name = name.split('.', 1)
553 section, name = name.split('.', 1)
544 if not section or not name:
554 if not section or not name:
545 raise IndexError
555 raise IndexError
546 ui.setconfig(section, name, value, '--config')
556 ui.setconfig(section, name, value, '--config')
547 configs.append((section, name, value))
557 configs.append((section, name, value))
548 except (IndexError, ValueError):
558 except (IndexError, ValueError):
549 raise util.Abort(_('malformed --config option: %r '
559 raise util.Abort(_('malformed --config option: %r '
550 '(use --config section.name=value)') % cfg)
560 '(use --config section.name=value)') % cfg)
551
561
552 return configs
562 return configs
553
563
554 def _earlygetopt(aliases, args):
564 def _earlygetopt(aliases, args):
555 """Return list of values for an option (or aliases).
565 """Return list of values for an option (or aliases).
556
566
557 The values are listed in the order they appear in args.
567 The values are listed in the order they appear in args.
558 The options and values are removed from args.
568 The options and values are removed from args.
559
569
560 >>> args = ['x', '--cwd', 'foo', 'y']
570 >>> args = ['x', '--cwd', 'foo', 'y']
561 >>> _earlygetopt(['--cwd'], args), args
571 >>> _earlygetopt(['--cwd'], args), args
562 (['foo'], ['x', 'y'])
572 (['foo'], ['x', 'y'])
563
573
564 >>> args = ['x', '--cwd=bar', 'y']
574 >>> args = ['x', '--cwd=bar', 'y']
565 >>> _earlygetopt(['--cwd'], args), args
575 >>> _earlygetopt(['--cwd'], args), args
566 (['bar'], ['x', 'y'])
576 (['bar'], ['x', 'y'])
567
577
568 >>> args = ['x', '-R', 'foo', 'y']
578 >>> args = ['x', '-R', 'foo', 'y']
569 >>> _earlygetopt(['-R'], args), args
579 >>> _earlygetopt(['-R'], args), args
570 (['foo'], ['x', 'y'])
580 (['foo'], ['x', 'y'])
571
581
572 >>> args = ['x', '-Rbar', 'y']
582 >>> args = ['x', '-Rbar', 'y']
573 >>> _earlygetopt(['-R'], args), args
583 >>> _earlygetopt(['-R'], args), args
574 (['bar'], ['x', 'y'])
584 (['bar'], ['x', 'y'])
575 """
585 """
576 try:
586 try:
577 argcount = args.index("--")
587 argcount = args.index("--")
578 except ValueError:
588 except ValueError:
579 argcount = len(args)
589 argcount = len(args)
580 shortopts = [opt for opt in aliases if len(opt) == 2]
590 shortopts = [opt for opt in aliases if len(opt) == 2]
581 values = []
591 values = []
582 pos = 0
592 pos = 0
583 while pos < argcount:
593 while pos < argcount:
584 fullarg = arg = args[pos]
594 fullarg = arg = args[pos]
585 equals = arg.find('=')
595 equals = arg.find('=')
586 if equals > -1:
596 if equals > -1:
587 arg = arg[:equals]
597 arg = arg[:equals]
588 if arg in aliases:
598 if arg in aliases:
589 del args[pos]
599 del args[pos]
590 if equals > -1:
600 if equals > -1:
591 values.append(fullarg[equals + 1:])
601 values.append(fullarg[equals + 1:])
592 argcount -= 1
602 argcount -= 1
593 else:
603 else:
594 if pos + 1 >= argcount:
604 if pos + 1 >= argcount:
595 # ignore and let getopt report an error if there is no value
605 # ignore and let getopt report an error if there is no value
596 break
606 break
597 values.append(args.pop(pos))
607 values.append(args.pop(pos))
598 argcount -= 2
608 argcount -= 2
599 elif arg[:2] in shortopts:
609 elif arg[:2] in shortopts:
600 # short option can have no following space, e.g. hg log -Rfoo
610 # short option can have no following space, e.g. hg log -Rfoo
601 values.append(args.pop(pos)[2:])
611 values.append(args.pop(pos)[2:])
602 argcount -= 1
612 argcount -= 1
603 else:
613 else:
604 pos += 1
614 pos += 1
605 return values
615 return values
606
616
607 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
617 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
608 # run pre-hook, and abort if it fails
618 # run pre-hook, and abort if it fails
609 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
619 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
610 pats=cmdpats, opts=cmdoptions)
620 pats=cmdpats, opts=cmdoptions)
611 ret = _runcommand(ui, options, cmd, d)
621 ret = _runcommand(ui, options, cmd, d)
612 # run post-hook, passing command result
622 # run post-hook, passing command result
613 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
623 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
614 result=ret, pats=cmdpats, opts=cmdoptions)
624 result=ret, pats=cmdpats, opts=cmdoptions)
615 return ret
625 return ret
616
626
617 def _getlocal(ui, rpath):
627 def _getlocal(ui, rpath):
618 """Return (path, local ui object) for the given target path.
628 """Return (path, local ui object) for the given target path.
619
629
620 Takes paths in [cwd]/.hg/hgrc into account."
630 Takes paths in [cwd]/.hg/hgrc into account."
621 """
631 """
622 try:
632 try:
623 wd = os.getcwd()
633 wd = os.getcwd()
624 except OSError, e:
634 except OSError, e:
625 raise util.Abort(_("error getting current working directory: %s") %
635 raise util.Abort(_("error getting current working directory: %s") %
626 e.strerror)
636 e.strerror)
627 path = cmdutil.findrepo(wd) or ""
637 path = cmdutil.findrepo(wd) or ""
628 if not path:
638 if not path:
629 lui = ui
639 lui = ui
630 else:
640 else:
631 lui = ui.copy()
641 lui = ui.copy()
632 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
642 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
633
643
634 if rpath and rpath[-1]:
644 if rpath and rpath[-1]:
635 path = lui.expandpath(rpath[-1])
645 path = lui.expandpath(rpath[-1])
636 lui = ui.copy()
646 lui = ui.copy()
637 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
647 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
638
648
639 return path, lui
649 return path, lui
640
650
641 def _checkshellalias(lui, ui, args, precheck=True):
651 def _checkshellalias(lui, ui, args, precheck=True):
642 """Return the function to run the shell alias, if it is required
652 """Return the function to run the shell alias, if it is required
643
653
644 'precheck' is whether this function is invoked before adding
654 'precheck' is whether this function is invoked before adding
645 aliases or not.
655 aliases or not.
646 """
656 """
647 options = {}
657 options = {}
648
658
649 try:
659 try:
650 args = fancyopts.fancyopts(args, commands.globalopts, options)
660 args = fancyopts.fancyopts(args, commands.globalopts, options)
651 except fancyopts.getopt.GetoptError:
661 except fancyopts.getopt.GetoptError:
652 return
662 return
653
663
654 if not args:
664 if not args:
655 return
665 return
656
666
657 if precheck:
667 if precheck:
658 strict = True
668 strict = True
659 norepo = commands.norepo
669 norepo = commands.norepo
660 optionalrepo = commands.optionalrepo
670 optionalrepo = commands.optionalrepo
661 def restorecommands():
671 def restorecommands():
662 commands.norepo = norepo
672 commands.norepo = norepo
663 commands.optionalrepo = optionalrepo
673 commands.optionalrepo = optionalrepo
664 cmdtable = commands.table.copy()
674 cmdtable = commands.table.copy()
665 addaliases(lui, cmdtable)
675 addaliases(lui, cmdtable)
666 else:
676 else:
667 strict = False
677 strict = False
668 def restorecommands():
678 def restorecommands():
669 pass
679 pass
670 cmdtable = commands.table
680 cmdtable = commands.table
671
681
672 cmd = args[0]
682 cmd = args[0]
673 try:
683 try:
674 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
684 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
675 except (error.AmbiguousCommand, error.UnknownCommand):
685 except (error.AmbiguousCommand, error.UnknownCommand):
676 restorecommands()
686 restorecommands()
677 return
687 return
678
688
679 cmd = aliases[0]
689 cmd = aliases[0]
680 fn = entry[0]
690 fn = entry[0]
681
691
682 if cmd and util.safehasattr(fn, 'shell'):
692 if cmd and util.safehasattr(fn, 'shell'):
683 d = lambda: fn(ui, *args[1:])
693 d = lambda: fn(ui, *args[1:])
684 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
694 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
685 [], {})
695 [], {})
686
696
687 restorecommands()
697 restorecommands()
688
698
689 _loaded = set()
699 _loaded = set()
690 def _dispatch(req):
700 def _dispatch(req):
691 args = req.args
701 args = req.args
692 ui = req.ui
702 ui = req.ui
693
703
694 # check for cwd
704 # check for cwd
695 cwd = _earlygetopt(['--cwd'], args)
705 cwd = _earlygetopt(['--cwd'], args)
696 if cwd:
706 if cwd:
697 os.chdir(cwd[-1])
707 os.chdir(cwd[-1])
698
708
699 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
709 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
700 path, lui = _getlocal(ui, rpath)
710 path, lui = _getlocal(ui, rpath)
701
711
702 # Now that we're operating in the right directory/repository with
712 # Now that we're operating in the right directory/repository with
703 # the right config settings, check for shell aliases
713 # the right config settings, check for shell aliases
704 shellaliasfn = _checkshellalias(lui, ui, args)
714 shellaliasfn = _checkshellalias(lui, ui, args)
705 if shellaliasfn:
715 if shellaliasfn:
706 return shellaliasfn()
716 return shellaliasfn()
707
717
708 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
718 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
709 # reposetup. Programs like TortoiseHg will call _dispatch several
719 # reposetup. Programs like TortoiseHg will call _dispatch several
710 # times so we keep track of configured extensions in _loaded.
720 # times so we keep track of configured extensions in _loaded.
711 extensions.loadall(lui)
721 extensions.loadall(lui)
712 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
722 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
713 # Propagate any changes to lui.__class__ by extensions
723 # Propagate any changes to lui.__class__ by extensions
714 ui.__class__ = lui.__class__
724 ui.__class__ = lui.__class__
715
725
716 # (uisetup and extsetup are handled in extensions.loadall)
726 # (uisetup and extsetup are handled in extensions.loadall)
717
727
718 for name, module in exts:
728 for name, module in exts:
719 cmdtable = getattr(module, 'cmdtable', {})
729 cmdtable = getattr(module, 'cmdtable', {})
720 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
730 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
721 if overrides:
731 if overrides:
722 ui.warn(_("extension '%s' overrides commands: %s\n")
732 ui.warn(_("extension '%s' overrides commands: %s\n")
723 % (name, " ".join(overrides)))
733 % (name, " ".join(overrides)))
724 commands.table.update(cmdtable)
734 commands.table.update(cmdtable)
725 _loaded.add(name)
735 _loaded.add(name)
726
736
727 # (reposetup is handled in hg.repository)
737 # (reposetup is handled in hg.repository)
728
738
729 addaliases(lui, commands.table)
739 addaliases(lui, commands.table)
730
740
731 if not lui.configbool("ui", "strict"):
741 if not lui.configbool("ui", "strict"):
732 # All aliases and commands are completely defined, now.
742 # All aliases and commands are completely defined, now.
733 # Check abbreviation/ambiguity of shell alias again, because shell
743 # Check abbreviation/ambiguity of shell alias again, because shell
734 # alias may cause failure of "_parse" (see issue4355)
744 # alias may cause failure of "_parse" (see issue4355)
735 shellaliasfn = _checkshellalias(lui, ui, args, precheck=False)
745 shellaliasfn = _checkshellalias(lui, ui, args, precheck=False)
736 if shellaliasfn:
746 if shellaliasfn:
737 return shellaliasfn()
747 return shellaliasfn()
738
748
739 # check for fallback encoding
749 # check for fallback encoding
740 fallback = lui.config('ui', 'fallbackencoding')
750 fallback = lui.config('ui', 'fallbackencoding')
741 if fallback:
751 if fallback:
742 encoding.fallbackencoding = fallback
752 encoding.fallbackencoding = fallback
743
753
744 fullargs = args
754 fullargs = args
745 cmd, func, args, options, cmdoptions = _parse(lui, args)
755 cmd, func, args, options, cmdoptions = _parse(lui, args)
746
756
747 if options["config"]:
757 if options["config"]:
748 raise util.Abort(_("option --config may not be abbreviated!"))
758 raise util.Abort(_("option --config may not be abbreviated!"))
749 if options["cwd"]:
759 if options["cwd"]:
750 raise util.Abort(_("option --cwd may not be abbreviated!"))
760 raise util.Abort(_("option --cwd may not be abbreviated!"))
751 if options["repository"]:
761 if options["repository"]:
752 raise util.Abort(_(
762 raise util.Abort(_(
753 "option -R has to be separated from other options (e.g. not -qR) "
763 "option -R has to be separated from other options (e.g. not -qR) "
754 "and --repository may only be abbreviated as --repo!"))
764 "and --repository may only be abbreviated as --repo!"))
755
765
756 if options["encoding"]:
766 if options["encoding"]:
757 encoding.encoding = options["encoding"]
767 encoding.encoding = options["encoding"]
758 if options["encodingmode"]:
768 if options["encodingmode"]:
759 encoding.encodingmode = options["encodingmode"]
769 encoding.encodingmode = options["encodingmode"]
760 if options["time"]:
770 if options["time"]:
761 def get_times():
771 def get_times():
762 t = os.times()
772 t = os.times()
763 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
773 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
764 t = (t[0], t[1], t[2], t[3], time.clock())
774 t = (t[0], t[1], t[2], t[3], time.clock())
765 return t
775 return t
766 s = get_times()
776 s = get_times()
767 def print_time():
777 def print_time():
768 t = get_times()
778 t = get_times()
769 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
779 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
770 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
780 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
771 atexit.register(print_time)
781 atexit.register(print_time)
772
782
773 uis = set([ui, lui])
783 uis = set([ui, lui])
774
784
775 if req.repo:
785 if req.repo:
776 uis.add(req.repo.ui)
786 uis.add(req.repo.ui)
777
787
778 if options['verbose'] or options['debug'] or options['quiet']:
788 if options['verbose'] or options['debug'] or options['quiet']:
779 for opt in ('verbose', 'debug', 'quiet'):
789 for opt in ('verbose', 'debug', 'quiet'):
780 val = str(bool(options[opt]))
790 val = str(bool(options[opt]))
781 for ui_ in uis:
791 for ui_ in uis:
782 ui_.setconfig('ui', opt, val, '--' + opt)
792 ui_.setconfig('ui', opt, val, '--' + opt)
783
793
784 if options['traceback']:
794 if options['traceback']:
785 for ui_ in uis:
795 for ui_ in uis:
786 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
796 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
787
797
788 if options['noninteractive']:
798 if options['noninteractive']:
789 for ui_ in uis:
799 for ui_ in uis:
790 ui_.setconfig('ui', 'interactive', 'off', '-y')
800 ui_.setconfig('ui', 'interactive', 'off', '-y')
791
801
792 if cmdoptions.get('insecure', False):
802 if cmdoptions.get('insecure', False):
793 for ui_ in uis:
803 for ui_ in uis:
794 ui_.setconfig('web', 'cacerts', '', '--insecure')
804 ui_.setconfig('web', 'cacerts', '', '--insecure')
795
805
796 if options['version']:
806 if options['version']:
797 return commands.version_(ui)
807 return commands.version_(ui)
798 if options['help']:
808 if options['help']:
799 return commands.help_(ui, cmd, command=True)
809 return commands.help_(ui, cmd, command=True)
800 elif not cmd:
810 elif not cmd:
801 return commands.help_(ui, 'shortlist')
811 return commands.help_(ui, 'shortlist')
802
812
803 repo = None
813 repo = None
804 cmdpats = args[:]
814 cmdpats = args[:]
805 if cmd not in commands.norepo.split():
815 if cmd not in commands.norepo.split():
806 # use the repo from the request only if we don't have -R
816 # use the repo from the request only if we don't have -R
807 if not rpath and not cwd:
817 if not rpath and not cwd:
808 repo = req.repo
818 repo = req.repo
809
819
810 if repo:
820 if repo:
811 # set the descriptors of the repo ui to those of ui
821 # set the descriptors of the repo ui to those of ui
812 repo.ui.fin = ui.fin
822 repo.ui.fin = ui.fin
813 repo.ui.fout = ui.fout
823 repo.ui.fout = ui.fout
814 repo.ui.ferr = ui.ferr
824 repo.ui.ferr = ui.ferr
815 else:
825 else:
816 try:
826 try:
817 repo = hg.repository(ui, path=path)
827 repo = hg.repository(ui, path=path)
818 if not repo.local():
828 if not repo.local():
819 raise util.Abort(_("repository '%s' is not local") % path)
829 raise util.Abort(_("repository '%s' is not local") % path)
820 repo.ui.setconfig("bundle", "mainreporoot", repo.root, 'repo')
830 repo.ui.setconfig("bundle", "mainreporoot", repo.root, 'repo')
821 except error.RequirementError:
831 except error.RequirementError:
822 raise
832 raise
823 except error.RepoError:
833 except error.RepoError:
824 if cmd not in commands.optionalrepo.split():
834 if cmd not in commands.optionalrepo.split():
825 if (cmd in commands.inferrepo.split() and
835 if (cmd in commands.inferrepo.split() and
826 args and not path): # try to infer -R from command args
836 args and not path): # try to infer -R from command args
827 repos = map(cmdutil.findrepo, args)
837 repos = map(cmdutil.findrepo, args)
828 guess = repos[0]
838 guess = repos[0]
829 if guess and repos.count(guess) == len(repos):
839 if guess and repos.count(guess) == len(repos):
830 req.args = ['--repository', guess] + fullargs
840 req.args = ['--repository', guess] + fullargs
831 return _dispatch(req)
841 return _dispatch(req)
832 if not path:
842 if not path:
833 raise error.RepoError(_("no repository found in '%s'"
843 raise error.RepoError(_("no repository found in '%s'"
834 " (.hg not found)")
844 " (.hg not found)")
835 % os.getcwd())
845 % os.getcwd())
836 raise
846 raise
837 if repo:
847 if repo:
838 ui = repo.ui
848 ui = repo.ui
839 if options['hidden']:
849 if options['hidden']:
840 repo = repo.unfiltered()
850 repo = repo.unfiltered()
841 args.insert(0, repo)
851 args.insert(0, repo)
842 elif rpath:
852 elif rpath:
843 ui.warn(_("warning: --repository ignored\n"))
853 ui.warn(_("warning: --repository ignored\n"))
844
854
845 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
855 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
846 ui.log("command", '%s\n', msg)
856 ui.log("command", '%s\n', msg)
847 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
857 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
848 try:
858 try:
849 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
859 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
850 cmdpats, cmdoptions)
860 cmdpats, cmdoptions)
851 finally:
861 finally:
852 if repo and repo != req.repo:
862 if repo and repo != req.repo:
853 repo.close()
863 repo.close()
854
864
855 def lsprofile(ui, func, fp):
865 def lsprofile(ui, func, fp):
856 format = ui.config('profiling', 'format', default='text')
866 format = ui.config('profiling', 'format', default='text')
857 field = ui.config('profiling', 'sort', default='inlinetime')
867 field = ui.config('profiling', 'sort', default='inlinetime')
858 limit = ui.configint('profiling', 'limit', default=30)
868 limit = ui.configint('profiling', 'limit', default=30)
859 climit = ui.configint('profiling', 'nested', default=5)
869 climit = ui.configint('profiling', 'nested', default=5)
860
870
861 if format not in ['text', 'kcachegrind']:
871 if format not in ['text', 'kcachegrind']:
862 ui.warn(_("unrecognized profiling format '%s'"
872 ui.warn(_("unrecognized profiling format '%s'"
863 " - Ignored\n") % format)
873 " - Ignored\n") % format)
864 format = 'text'
874 format = 'text'
865
875
866 try:
876 try:
867 from mercurial import lsprof
877 from mercurial import lsprof
868 except ImportError:
878 except ImportError:
869 raise util.Abort(_(
879 raise util.Abort(_(
870 'lsprof not available - install from '
880 'lsprof not available - install from '
871 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
881 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
872 p = lsprof.Profiler()
882 p = lsprof.Profiler()
873 p.enable(subcalls=True)
883 p.enable(subcalls=True)
874 try:
884 try:
875 return func()
885 return func()
876 finally:
886 finally:
877 p.disable()
887 p.disable()
878
888
879 if format == 'kcachegrind':
889 if format == 'kcachegrind':
880 import lsprofcalltree
890 import lsprofcalltree
881 calltree = lsprofcalltree.KCacheGrind(p)
891 calltree = lsprofcalltree.KCacheGrind(p)
882 calltree.output(fp)
892 calltree.output(fp)
883 else:
893 else:
884 # format == 'text'
894 # format == 'text'
885 stats = lsprof.Stats(p.getstats())
895 stats = lsprof.Stats(p.getstats())
886 stats.sort(field)
896 stats.sort(field)
887 stats.pprint(limit=limit, file=fp, climit=climit)
897 stats.pprint(limit=limit, file=fp, climit=climit)
888
898
889 def statprofile(ui, func, fp):
899 def statprofile(ui, func, fp):
890 try:
900 try:
891 import statprof
901 import statprof
892 except ImportError:
902 except ImportError:
893 raise util.Abort(_(
903 raise util.Abort(_(
894 'statprof not available - install using "easy_install statprof"'))
904 'statprof not available - install using "easy_install statprof"'))
895
905
896 freq = ui.configint('profiling', 'freq', default=1000)
906 freq = ui.configint('profiling', 'freq', default=1000)
897 if freq > 0:
907 if freq > 0:
898 statprof.reset(freq)
908 statprof.reset(freq)
899 else:
909 else:
900 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
910 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
901
911
902 statprof.start()
912 statprof.start()
903 try:
913 try:
904 return func()
914 return func()
905 finally:
915 finally:
906 statprof.stop()
916 statprof.stop()
907 statprof.display(fp)
917 statprof.display(fp)
908
918
909 def _runcommand(ui, options, cmd, cmdfunc):
919 def _runcommand(ui, options, cmd, cmdfunc):
910 def checkargs():
920 def checkargs():
911 try:
921 try:
912 return cmdfunc()
922 return cmdfunc()
913 except error.SignatureError:
923 except error.SignatureError:
914 raise error.CommandError(cmd, _("invalid arguments"))
924 raise error.CommandError(cmd, _("invalid arguments"))
915
925
916 if options['profile']:
926 if options['profile']:
917 profiler = os.getenv('HGPROF')
927 profiler = os.getenv('HGPROF')
918 if profiler is None:
928 if profiler is None:
919 profiler = ui.config('profiling', 'type', default='ls')
929 profiler = ui.config('profiling', 'type', default='ls')
920 if profiler not in ('ls', 'stat'):
930 if profiler not in ('ls', 'stat'):
921 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
931 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
922 profiler = 'ls'
932 profiler = 'ls'
923
933
924 output = ui.config('profiling', 'output')
934 output = ui.config('profiling', 'output')
925
935
926 if output:
936 if output:
927 path = ui.expandpath(output)
937 path = ui.expandpath(output)
928 fp = open(path, 'wb')
938 fp = open(path, 'wb')
929 else:
939 else:
930 fp = sys.stderr
940 fp = sys.stderr
931
941
932 try:
942 try:
933 if profiler == 'ls':
943 if profiler == 'ls':
934 return lsprofile(ui, checkargs, fp)
944 return lsprofile(ui, checkargs, fp)
935 else:
945 else:
936 return statprofile(ui, checkargs, fp)
946 return statprofile(ui, checkargs, fp)
937 finally:
947 finally:
938 if output:
948 if output:
939 fp.close()
949 fp.close()
940 else:
950 else:
941 return checkargs()
951 return checkargs()
@@ -1,1131 +1,1142 b''
1 Test basic extension support
1 Test basic extension support
2
2
3 $ cat > foobar.py <<EOF
3 $ cat > foobar.py <<EOF
4 > import os
4 > import os
5 > from mercurial import cmdutil, commands
5 > from mercurial import cmdutil, commands
6 > cmdtable = {}
6 > cmdtable = {}
7 > command = cmdutil.command(cmdtable)
7 > command = cmdutil.command(cmdtable)
8 > def uisetup(ui):
8 > def uisetup(ui):
9 > ui.write("uisetup called\\n")
9 > ui.write("uisetup called\\n")
10 > def reposetup(ui, repo):
10 > def reposetup(ui, repo):
11 > ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
11 > ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
12 > ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
12 > ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
13 > @command('foo', [], 'hg foo')
13 > @command('foo', [], 'hg foo')
14 > def foo(ui, *args, **kwargs):
14 > def foo(ui, *args, **kwargs):
15 > ui.write("Foo\\n")
15 > ui.write("Foo\\n")
16 > @command('bar', [], 'hg bar', norepo=True)
16 > @command('bar', [], 'hg bar', norepo=True)
17 > def bar(ui, *args, **kwargs):
17 > def bar(ui, *args, **kwargs):
18 > ui.write("Bar\\n")
18 > ui.write("Bar\\n")
19 > EOF
19 > EOF
20 $ abspath=`pwd`/foobar.py
20 $ abspath=`pwd`/foobar.py
21
21
22 $ mkdir barfoo
22 $ mkdir barfoo
23 $ cp foobar.py barfoo/__init__.py
23 $ cp foobar.py barfoo/__init__.py
24 $ barfoopath=`pwd`/barfoo
24 $ barfoopath=`pwd`/barfoo
25
25
26 $ hg init a
26 $ hg init a
27 $ cd a
27 $ cd a
28 $ echo foo > file
28 $ echo foo > file
29 $ hg add file
29 $ hg add file
30 $ hg commit -m 'add file'
30 $ hg commit -m 'add file'
31
31
32 $ echo '[extensions]' >> $HGRCPATH
32 $ echo '[extensions]' >> $HGRCPATH
33 $ echo "foobar = $abspath" >> $HGRCPATH
33 $ echo "foobar = $abspath" >> $HGRCPATH
34 $ hg foo
34 $ hg foo
35 uisetup called
35 uisetup called
36 reposetup called for a
36 reposetup called for a
37 ui == repo.ui
37 ui == repo.ui
38 Foo
38 Foo
39
39
40 $ cd ..
40 $ cd ..
41 $ hg clone a b
41 $ hg clone a b
42 uisetup called
42 uisetup called
43 reposetup called for a
43 reposetup called for a
44 ui == repo.ui
44 ui == repo.ui
45 reposetup called for b
45 reposetup called for b
46 ui == repo.ui
46 ui == repo.ui
47 updating to branch default
47 updating to branch default
48 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
48 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
49
49
50 $ hg bar
50 $ hg bar
51 uisetup called
51 uisetup called
52 Bar
52 Bar
53 $ echo 'foobar = !' >> $HGRCPATH
53 $ echo 'foobar = !' >> $HGRCPATH
54
54
55 module/__init__.py-style
55 module/__init__.py-style
56
56
57 $ echo "barfoo = $barfoopath" >> $HGRCPATH
57 $ echo "barfoo = $barfoopath" >> $HGRCPATH
58 $ cd a
58 $ cd a
59 $ hg foo
59 $ hg foo
60 uisetup called
60 uisetup called
61 reposetup called for a
61 reposetup called for a
62 ui == repo.ui
62 ui == repo.ui
63 Foo
63 Foo
64 $ echo 'barfoo = !' >> $HGRCPATH
64 $ echo 'barfoo = !' >> $HGRCPATH
65
65
66 Check that extensions are loaded in phases:
66 Check that extensions are loaded in phases:
67
67
68 $ cat > foo.py <<EOF
68 $ cat > foo.py <<EOF
69 > import os
69 > import os
70 > name = os.path.basename(__file__).rsplit('.', 1)[0]
70 > name = os.path.basename(__file__).rsplit('.', 1)[0]
71 > print "1) %s imported" % name
71 > print "1) %s imported" % name
72 > def uisetup(ui):
72 > def uisetup(ui):
73 > print "2) %s uisetup" % name
73 > print "2) %s uisetup" % name
74 > def extsetup():
74 > def extsetup():
75 > print "3) %s extsetup" % name
75 > print "3) %s extsetup" % name
76 > def reposetup(ui, repo):
76 > def reposetup(ui, repo):
77 > print "4) %s reposetup" % name
77 > print "4) %s reposetup" % name
78 > EOF
78 > EOF
79
79
80 $ cp foo.py bar.py
80 $ cp foo.py bar.py
81 $ echo 'foo = foo.py' >> $HGRCPATH
81 $ echo 'foo = foo.py' >> $HGRCPATH
82 $ echo 'bar = bar.py' >> $HGRCPATH
82 $ echo 'bar = bar.py' >> $HGRCPATH
83
83
84 Command with no output, we just want to see the extensions loaded:
84 Command with no output, we just want to see the extensions loaded:
85
85
86 $ hg paths
86 $ hg paths
87 1) foo imported
87 1) foo imported
88 1) bar imported
88 1) bar imported
89 2) foo uisetup
89 2) foo uisetup
90 2) bar uisetup
90 2) bar uisetup
91 3) foo extsetup
91 3) foo extsetup
92 3) bar extsetup
92 3) bar extsetup
93 4) foo reposetup
93 4) foo reposetup
94 4) bar reposetup
94 4) bar reposetup
95
95
96 Check hgweb's load order:
96 Check hgweb's load order:
97
97
98 $ cat > hgweb.cgi <<EOF
98 $ cat > hgweb.cgi <<EOF
99 > #!/usr/bin/env python
99 > #!/usr/bin/env python
100 > from mercurial import demandimport; demandimport.enable()
100 > from mercurial import demandimport; demandimport.enable()
101 > from mercurial.hgweb import hgweb
101 > from mercurial.hgweb import hgweb
102 > from mercurial.hgweb import wsgicgi
102 > from mercurial.hgweb import wsgicgi
103 > application = hgweb('.', 'test repo')
103 > application = hgweb('.', 'test repo')
104 > wsgicgi.launch(application)
104 > wsgicgi.launch(application)
105 > EOF
105 > EOF
106
106
107 $ REQUEST_METHOD='GET' PATH_INFO='/' SCRIPT_NAME='' QUERY_STRING='' \
107 $ REQUEST_METHOD='GET' PATH_INFO='/' SCRIPT_NAME='' QUERY_STRING='' \
108 > SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \
108 > SERVER_PORT='80' SERVER_NAME='localhost' python hgweb.cgi \
109 > | grep '^[0-9]) ' # ignores HTML output
109 > | grep '^[0-9]) ' # ignores HTML output
110 1) foo imported
110 1) foo imported
111 1) bar imported
111 1) bar imported
112 2) foo uisetup
112 2) foo uisetup
113 2) bar uisetup
113 2) bar uisetup
114 3) foo extsetup
114 3) foo extsetup
115 3) bar extsetup
115 3) bar extsetup
116 4) foo reposetup
116 4) foo reposetup
117 4) bar reposetup
117 4) bar reposetup
118 4) foo reposetup
118 4) foo reposetup
119 4) bar reposetup
119 4) bar reposetup
120
120
121 $ echo 'foo = !' >> $HGRCPATH
121 $ echo 'foo = !' >> $HGRCPATH
122 $ echo 'bar = !' >> $HGRCPATH
122 $ echo 'bar = !' >> $HGRCPATH
123
123
124 Check "from __future__ import absolute_import" support for external libraries
124 Check "from __future__ import absolute_import" support for external libraries
125
125
126 #if windows
126 #if windows
127 $ PATHSEP=";"
127 $ PATHSEP=";"
128 #else
128 #else
129 $ PATHSEP=":"
129 $ PATHSEP=":"
130 #endif
130 #endif
131 $ export PATHSEP
131 $ export PATHSEP
132
132
133 $ mkdir $TESTTMP/libroot
133 $ mkdir $TESTTMP/libroot
134 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
134 $ echo "s = 'libroot/ambig.py'" > $TESTTMP/libroot/ambig.py
135 $ mkdir $TESTTMP/libroot/mod
135 $ mkdir $TESTTMP/libroot/mod
136 $ touch $TESTTMP/libroot/mod/__init__.py
136 $ touch $TESTTMP/libroot/mod/__init__.py
137 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
137 $ echo "s = 'libroot/mod/ambig.py'" > $TESTTMP/libroot/mod/ambig.py
138
138
139 #if absimport
139 #if absimport
140 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
140 $ cat > $TESTTMP/libroot/mod/ambigabs.py <<EOF
141 > from __future__ import absolute_import
141 > from __future__ import absolute_import
142 > import ambig # should load "libroot/ambig.py"
142 > import ambig # should load "libroot/ambig.py"
143 > s = ambig.s
143 > s = ambig.s
144 > EOF
144 > EOF
145 $ cat > loadabs.py <<EOF
145 $ cat > loadabs.py <<EOF
146 > import mod.ambigabs as ambigabs
146 > import mod.ambigabs as ambigabs
147 > def extsetup():
147 > def extsetup():
148 > print 'ambigabs.s=%s' % ambigabs.s
148 > print 'ambigabs.s=%s' % ambigabs.s
149 > EOF
149 > EOF
150 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
150 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadabs=loadabs.py root)
151 ambigabs.s=libroot/ambig.py
151 ambigabs.s=libroot/ambig.py
152 $TESTTMP/a (glob)
152 $TESTTMP/a (glob)
153 #endif
153 #endif
154
154
155 #if no-py3k
155 #if no-py3k
156 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
156 $ cat > $TESTTMP/libroot/mod/ambigrel.py <<EOF
157 > import ambig # should load "libroot/mod/ambig.py"
157 > import ambig # should load "libroot/mod/ambig.py"
158 > s = ambig.s
158 > s = ambig.s
159 > EOF
159 > EOF
160 $ cat > loadrel.py <<EOF
160 $ cat > loadrel.py <<EOF
161 > import mod.ambigrel as ambigrel
161 > import mod.ambigrel as ambigrel
162 > def extsetup():
162 > def extsetup():
163 > print 'ambigrel.s=%s' % ambigrel.s
163 > print 'ambigrel.s=%s' % ambigrel.s
164 > EOF
164 > EOF
165 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
165 $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}/libroot; hg --config extensions.loadrel=loadrel.py root)
166 ambigrel.s=libroot/mod/ambig.py
166 ambigrel.s=libroot/mod/ambig.py
167 $TESTTMP/a (glob)
167 $TESTTMP/a (glob)
168 #endif
168 #endif
169
169
170 Check absolute/relative import of extension specific modules
170 Check absolute/relative import of extension specific modules
171
171
172 $ mkdir $TESTTMP/extroot
172 $ mkdir $TESTTMP/extroot
173 $ cat > $TESTTMP/extroot/bar.py <<EOF
173 $ cat > $TESTTMP/extroot/bar.py <<EOF
174 > s = 'this is extroot.bar'
174 > s = 'this is extroot.bar'
175 > EOF
175 > EOF
176 $ mkdir $TESTTMP/extroot/sub1
176 $ mkdir $TESTTMP/extroot/sub1
177 $ cat > $TESTTMP/extroot/sub1/__init__.py <<EOF
177 $ cat > $TESTTMP/extroot/sub1/__init__.py <<EOF
178 > s = 'this is extroot.sub1.__init__'
178 > s = 'this is extroot.sub1.__init__'
179 > EOF
179 > EOF
180 $ cat > $TESTTMP/extroot/sub1/baz.py <<EOF
180 $ cat > $TESTTMP/extroot/sub1/baz.py <<EOF
181 > s = 'this is extroot.sub1.baz'
181 > s = 'this is extroot.sub1.baz'
182 > EOF
182 > EOF
183 $ cat > $TESTTMP/extroot/__init__.py <<EOF
183 $ cat > $TESTTMP/extroot/__init__.py <<EOF
184 > s = 'this is extroot.__init__'
184 > s = 'this is extroot.__init__'
185 > import foo
185 > import foo
186 > def extsetup(ui):
186 > def extsetup(ui):
187 > ui.write('(extroot) ', foo.func(), '\n')
187 > ui.write('(extroot) ', foo.func(), '\n')
188 > EOF
188 > EOF
189
189
190 $ cat > $TESTTMP/extroot/foo.py <<EOF
190 $ cat > $TESTTMP/extroot/foo.py <<EOF
191 > # test absolute import
191 > # test absolute import
192 > buf = []
192 > buf = []
193 > def func():
193 > def func():
194 > # "not locals" case
194 > # "not locals" case
195 > import extroot.bar
195 > import extroot.bar
196 > buf.append('import extroot.bar in func(): %s' % extroot.bar.s)
196 > buf.append('import extroot.bar in func(): %s' % extroot.bar.s)
197 > return '\n(extroot) '.join(buf)
197 > return '\n(extroot) '.join(buf)
198 > # "fromlist == ('*',)" case
198 > # "fromlist == ('*',)" case
199 > from extroot.bar import *
199 > from extroot.bar import *
200 > buf.append('from extroot.bar import *: %s' % s)
200 > buf.append('from extroot.bar import *: %s' % s)
201 > # "not fromlist" and "if '.' in name" case
201 > # "not fromlist" and "if '.' in name" case
202 > import extroot.sub1.baz
202 > import extroot.sub1.baz
203 > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
203 > buf.append('import extroot.sub1.baz: %s' % extroot.sub1.baz.s)
204 > # "not fromlist" and NOT "if '.' in name" case
204 > # "not fromlist" and NOT "if '.' in name" case
205 > import extroot
205 > import extroot
206 > buf.append('import extroot: %s' % extroot.s)
206 > buf.append('import extroot: %s' % extroot.s)
207 > # NOT "not fromlist" and NOT "level != -1" case
207 > # NOT "not fromlist" and NOT "level != -1" case
208 > from extroot.bar import s
208 > from extroot.bar import s
209 > buf.append('from extroot.bar import s: %s' % s)
209 > buf.append('from extroot.bar import s: %s' % s)
210 > EOF
210 > EOF
211 $ hg --config extensions.extroot=$TESTTMP/extroot root
211 $ hg --config extensions.extroot=$TESTTMP/extroot root
212 (extroot) from extroot.bar import *: this is extroot.bar
212 (extroot) from extroot.bar import *: this is extroot.bar
213 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
213 (extroot) import extroot.sub1.baz: this is extroot.sub1.baz
214 (extroot) import extroot: this is extroot.__init__
214 (extroot) import extroot: this is extroot.__init__
215 (extroot) from extroot.bar import s: this is extroot.bar
215 (extroot) from extroot.bar import s: this is extroot.bar
216 (extroot) import extroot.bar in func(): this is extroot.bar
216 (extroot) import extroot.bar in func(): this is extroot.bar
217 $TESTTMP/a (glob)
217 $TESTTMP/a (glob)
218
218
219 #if no-py3k
219 #if no-py3k
220 $ rm "$TESTTMP"/extroot/foo.*
220 $ rm "$TESTTMP"/extroot/foo.*
221 $ cat > $TESTTMP/extroot/foo.py <<EOF
221 $ cat > $TESTTMP/extroot/foo.py <<EOF
222 > # test relative import
222 > # test relative import
223 > buf = []
223 > buf = []
224 > def func():
224 > def func():
225 > # "not locals" case
225 > # "not locals" case
226 > import bar
226 > import bar
227 > buf.append('import bar in func(): %s' % bar.s)
227 > buf.append('import bar in func(): %s' % bar.s)
228 > return '\n(extroot) '.join(buf)
228 > return '\n(extroot) '.join(buf)
229 > # "fromlist == ('*',)" case
229 > # "fromlist == ('*',)" case
230 > from bar import *
230 > from bar import *
231 > buf.append('from bar import *: %s' % s)
231 > buf.append('from bar import *: %s' % s)
232 > # "not fromlist" and "if '.' in name" case
232 > # "not fromlist" and "if '.' in name" case
233 > import sub1.baz
233 > import sub1.baz
234 > buf.append('import sub1.baz: %s' % sub1.baz.s)
234 > buf.append('import sub1.baz: %s' % sub1.baz.s)
235 > # "not fromlist" and NOT "if '.' in name" case
235 > # "not fromlist" and NOT "if '.' in name" case
236 > import sub1
236 > import sub1
237 > buf.append('import sub1: %s' % sub1.s)
237 > buf.append('import sub1: %s' % sub1.s)
238 > # NOT "not fromlist" and NOT "level != -1" case
238 > # NOT "not fromlist" and NOT "level != -1" case
239 > from bar import s
239 > from bar import s
240 > buf.append('from bar import s: %s' % s)
240 > buf.append('from bar import s: %s' % s)
241 > EOF
241 > EOF
242 $ hg --config extensions.extroot=$TESTTMP/extroot root
242 $ hg --config extensions.extroot=$TESTTMP/extroot root
243 (extroot) from bar import *: this is extroot.bar
243 (extroot) from bar import *: this is extroot.bar
244 (extroot) import sub1.baz: this is extroot.sub1.baz
244 (extroot) import sub1.baz: this is extroot.sub1.baz
245 (extroot) import sub1: this is extroot.sub1.__init__
245 (extroot) import sub1: this is extroot.sub1.__init__
246 (extroot) from bar import s: this is extroot.bar
246 (extroot) from bar import s: this is extroot.bar
247 (extroot) import bar in func(): this is extroot.bar
247 (extroot) import bar in func(): this is extroot.bar
248 $TESTTMP/a (glob)
248 $TESTTMP/a (glob)
249 #endif
249 #endif
250
250
251 $ cd ..
251 $ cd ..
252
252
253 hide outer repo
253 hide outer repo
254 $ hg init
254 $ hg init
255
255
256 $ cat > empty.py <<EOF
256 $ cat > empty.py <<EOF
257 > '''empty cmdtable
257 > '''empty cmdtable
258 > '''
258 > '''
259 > cmdtable = {}
259 > cmdtable = {}
260 > EOF
260 > EOF
261 $ emptypath=`pwd`/empty.py
261 $ emptypath=`pwd`/empty.py
262 $ echo "empty = $emptypath" >> $HGRCPATH
262 $ echo "empty = $emptypath" >> $HGRCPATH
263 $ hg help empty
263 $ hg help empty
264 empty extension - empty cmdtable
264 empty extension - empty cmdtable
265
265
266 no commands defined
266 no commands defined
267
267
268
268
269 $ echo 'empty = !' >> $HGRCPATH
269 $ echo 'empty = !' >> $HGRCPATH
270
270
271 $ cat > debugextension.py <<EOF
271 $ cat > debugextension.py <<EOF
272 > '''only debugcommands
272 > '''only debugcommands
273 > '''
273 > '''
274 > from mercurial import cmdutil
274 > from mercurial import cmdutil
275 > cmdtable = {}
275 > cmdtable = {}
276 > command = cmdutil.command(cmdtable)
276 > command = cmdutil.command(cmdtable)
277 > @command('debugfoobar', [], 'hg debugfoobar')
277 > @command('debugfoobar', [], 'hg debugfoobar')
278 > def debugfoobar(ui, repo, *args, **opts):
278 > def debugfoobar(ui, repo, *args, **opts):
279 > "yet another debug command"
279 > "yet another debug command"
280 > pass
280 > pass
281 > @command('foo', [], 'hg foo')
281 > @command('foo', [], 'hg foo')
282 > def foo(ui, repo, *args, **opts):
282 > def foo(ui, repo, *args, **opts):
283 > """yet another foo command
283 > """yet another foo command
284 > This command has been DEPRECATED since forever.
284 > This command has been DEPRECATED since forever.
285 > """
285 > """
286 > pass
286 > pass
287 > EOF
287 > EOF
288 $ debugpath=`pwd`/debugextension.py
288 $ debugpath=`pwd`/debugextension.py
289 $ echo "debugextension = $debugpath" >> $HGRCPATH
289 $ echo "debugextension = $debugpath" >> $HGRCPATH
290
290
291 $ hg help debugextension
291 $ hg help debugextension
292 debugextension extension - only debugcommands
292 debugextension extension - only debugcommands
293
293
294 no commands defined
294 no commands defined
295
295
296
296
297 $ hg --verbose help debugextension
297 $ hg --verbose help debugextension
298 debugextension extension - only debugcommands
298 debugextension extension - only debugcommands
299
299
300 list of commands:
300 list of commands:
301
301
302 foo yet another foo command
302 foo yet another foo command
303
303
304 global options ([+] can be repeated):
304 global options ([+] can be repeated):
305
305
306 -R --repository REPO repository root directory or name of overlay bundle
306 -R --repository REPO repository root directory or name of overlay bundle
307 file
307 file
308 --cwd DIR change working directory
308 --cwd DIR change working directory
309 -y --noninteractive do not prompt, automatically pick the first choice for
309 -y --noninteractive do not prompt, automatically pick the first choice for
310 all prompts
310 all prompts
311 -q --quiet suppress output
311 -q --quiet suppress output
312 -v --verbose enable additional output
312 -v --verbose enable additional output
313 --config CONFIG [+] set/override config option (use 'section.name=value')
313 --config CONFIG [+] set/override config option (use 'section.name=value')
314 --debug enable debugging output
314 --debug enable debugging output
315 --debugger start debugger
315 --debugger start debugger
316 --encoding ENCODE set the charset encoding (default: ascii)
316 --encoding ENCODE set the charset encoding (default: ascii)
317 --encodingmode MODE set the charset encoding mode (default: strict)
317 --encodingmode MODE set the charset encoding mode (default: strict)
318 --traceback always print a traceback on exception
318 --traceback always print a traceback on exception
319 --time time how long the command takes
319 --time time how long the command takes
320 --profile print command execution profile
320 --profile print command execution profile
321 --version output version information and exit
321 --version output version information and exit
322 -h --help display help and exit
322 -h --help display help and exit
323 --hidden consider hidden changesets
323 --hidden consider hidden changesets
324
324
325
325
326
326
327
327
328
328
329
329
330 $ hg --debug help debugextension
330 $ hg --debug help debugextension
331 debugextension extension - only debugcommands
331 debugextension extension - only debugcommands
332
332
333 list of commands:
333 list of commands:
334
334
335 debugfoobar yet another debug command
335 debugfoobar yet another debug command
336 foo yet another foo command
336 foo yet another foo command
337
337
338 global options ([+] can be repeated):
338 global options ([+] can be repeated):
339
339
340 -R --repository REPO repository root directory or name of overlay bundle
340 -R --repository REPO repository root directory or name of overlay bundle
341 file
341 file
342 --cwd DIR change working directory
342 --cwd DIR change working directory
343 -y --noninteractive do not prompt, automatically pick the first choice for
343 -y --noninteractive do not prompt, automatically pick the first choice for
344 all prompts
344 all prompts
345 -q --quiet suppress output
345 -q --quiet suppress output
346 -v --verbose enable additional output
346 -v --verbose enable additional output
347 --config CONFIG [+] set/override config option (use 'section.name=value')
347 --config CONFIG [+] set/override config option (use 'section.name=value')
348 --debug enable debugging output
348 --debug enable debugging output
349 --debugger start debugger
349 --debugger start debugger
350 --encoding ENCODE set the charset encoding (default: ascii)
350 --encoding ENCODE set the charset encoding (default: ascii)
351 --encodingmode MODE set the charset encoding mode (default: strict)
351 --encodingmode MODE set the charset encoding mode (default: strict)
352 --traceback always print a traceback on exception
352 --traceback always print a traceback on exception
353 --time time how long the command takes
353 --time time how long the command takes
354 --profile print command execution profile
354 --profile print command execution profile
355 --version output version information and exit
355 --version output version information and exit
356 -h --help display help and exit
356 -h --help display help and exit
357 --hidden consider hidden changesets
357 --hidden consider hidden changesets
358
358
359
359
360
360
361
361
362
362
363 $ echo 'debugextension = !' >> $HGRCPATH
363 $ echo 'debugextension = !' >> $HGRCPATH
364
364
365 Extension module help vs command help:
365 Extension module help vs command help:
366
366
367 $ echo 'extdiff =' >> $HGRCPATH
367 $ echo 'extdiff =' >> $HGRCPATH
368 $ hg help extdiff
368 $ hg help extdiff
369 hg extdiff [OPT]... [FILE]...
369 hg extdiff [OPT]... [FILE]...
370
370
371 use external program to diff repository (or selected files)
371 use external program to diff repository (or selected files)
372
372
373 Show differences between revisions for the specified files, using an
373 Show differences between revisions for the specified files, using an
374 external program. The default program used is diff, with default options
374 external program. The default program used is diff, with default options
375 "-Npru".
375 "-Npru".
376
376
377 To select a different program, use the -p/--program option. The program
377 To select a different program, use the -p/--program option. The program
378 will be passed the names of two directories to compare. To pass additional
378 will be passed the names of two directories to compare. To pass additional
379 options to the program, use -o/--option. These will be passed before the
379 options to the program, use -o/--option. These will be passed before the
380 names of the directories to compare.
380 names of the directories to compare.
381
381
382 When two revision arguments are given, then changes are shown between
382 When two revision arguments are given, then changes are shown between
383 those revisions. If only one revision is specified then that revision is
383 those revisions. If only one revision is specified then that revision is
384 compared to the working directory, and, when no revisions are specified,
384 compared to the working directory, and, when no revisions are specified,
385 the working directory files are compared to its parent.
385 the working directory files are compared to its parent.
386
386
387 (use "hg help -e extdiff" to show help for the extdiff extension)
387 (use "hg help -e extdiff" to show help for the extdiff extension)
388
388
389 options ([+] can be repeated):
389 options ([+] can be repeated):
390
390
391 -p --program CMD comparison program to run
391 -p --program CMD comparison program to run
392 -o --option OPT [+] pass option to comparison program
392 -o --option OPT [+] pass option to comparison program
393 -r --rev REV [+] revision
393 -r --rev REV [+] revision
394 -c --change REV change made by revision
394 -c --change REV change made by revision
395 -I --include PATTERN [+] include names matching the given patterns
395 -I --include PATTERN [+] include names matching the given patterns
396 -X --exclude PATTERN [+] exclude names matching the given patterns
396 -X --exclude PATTERN [+] exclude names matching the given patterns
397
397
398 (some details hidden, use --verbose to show complete help)
398 (some details hidden, use --verbose to show complete help)
399
399
400
400
401
401
402
402
403
403
404
404
405
405
406
406
407
407
408
408
409 $ hg help --extension extdiff
409 $ hg help --extension extdiff
410 extdiff extension - command to allow external programs to compare revisions
410 extdiff extension - command to allow external programs to compare revisions
411
411
412 The extdiff Mercurial extension allows you to use external programs to compare
412 The extdiff Mercurial extension allows you to use external programs to compare
413 revisions, or revision with working directory. The external diff programs are
413 revisions, or revision with working directory. The external diff programs are
414 called with a configurable set of options and two non-option arguments: paths
414 called with a configurable set of options and two non-option arguments: paths
415 to directories containing snapshots of files to compare.
415 to directories containing snapshots of files to compare.
416
416
417 The extdiff extension also allows you to configure new diff commands, so you
417 The extdiff extension also allows you to configure new diff commands, so you
418 do not need to type "hg extdiff -p kdiff3" always.
418 do not need to type "hg extdiff -p kdiff3" always.
419
419
420 [extdiff]
420 [extdiff]
421 # add new command that runs GNU diff(1) in 'context diff' mode
421 # add new command that runs GNU diff(1) in 'context diff' mode
422 cdiff = gdiff -Nprc5
422 cdiff = gdiff -Nprc5
423 ## or the old way:
423 ## or the old way:
424 #cmd.cdiff = gdiff
424 #cmd.cdiff = gdiff
425 #opts.cdiff = -Nprc5
425 #opts.cdiff = -Nprc5
426
426
427 # add new command called meld, runs meld (no need to name twice). If
427 # add new command called meld, runs meld (no need to name twice). If
428 # the meld executable is not available, the meld tool in [merge-tools]
428 # the meld executable is not available, the meld tool in [merge-tools]
429 # will be used, if available
429 # will be used, if available
430 meld =
430 meld =
431
431
432 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
432 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
433 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
433 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
434 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
434 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
435 # your .vimrc
435 # your .vimrc
436 vimdiff = gvim -f "+next" \
436 vimdiff = gvim -f "+next" \
437 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
437 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
438
438
439 Tool arguments can include variables that are expanded at runtime:
439 Tool arguments can include variables that are expanded at runtime:
440
440
441 $parent1, $plabel1 - filename, descriptive label of first parent
441 $parent1, $plabel1 - filename, descriptive label of first parent
442 $child, $clabel - filename, descriptive label of child revision
442 $child, $clabel - filename, descriptive label of child revision
443 $parent2, $plabel2 - filename, descriptive label of second parent
443 $parent2, $plabel2 - filename, descriptive label of second parent
444 $root - repository root
444 $root - repository root
445 $parent is an alias for $parent1.
445 $parent is an alias for $parent1.
446
446
447 The extdiff extension will look in your [diff-tools] and [merge-tools]
447 The extdiff extension will look in your [diff-tools] and [merge-tools]
448 sections for diff tool arguments, when none are specified in [extdiff].
448 sections for diff tool arguments, when none are specified in [extdiff].
449
449
450 [extdiff]
450 [extdiff]
451 kdiff3 =
451 kdiff3 =
452
452
453 [diff-tools]
453 [diff-tools]
454 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
454 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
455
455
456 You can use -I/-X and list of file or directory names like normal "hg diff"
456 You can use -I/-X and list of file or directory names like normal "hg diff"
457 command. The extdiff extension makes snapshots of only needed files, so
457 command. The extdiff extension makes snapshots of only needed files, so
458 running the external diff program will actually be pretty fast (at least
458 running the external diff program will actually be pretty fast (at least
459 faster than having to compare the entire tree).
459 faster than having to compare the entire tree).
460
460
461 list of commands:
461 list of commands:
462
462
463 extdiff use external program to diff repository (or selected files)
463 extdiff use external program to diff repository (or selected files)
464
464
465 (use "hg help -v -e extdiff" to show built-in aliases and global options)
465 (use "hg help -v -e extdiff" to show built-in aliases and global options)
466
466
467
467
468
468
469
469
470
470
471
471
472
472
473
473
474
474
475
475
476
476
477
477
478
478
479
479
480
480
481
481
482 $ echo 'extdiff = !' >> $HGRCPATH
482 $ echo 'extdiff = !' >> $HGRCPATH
483
483
484 Test help topic with same name as extension
484 Test help topic with same name as extension
485
485
486 $ cat > multirevs.py <<EOF
486 $ cat > multirevs.py <<EOF
487 > from mercurial import cmdutil, commands
487 > from mercurial import cmdutil, commands
488 > cmdtable = {}
488 > cmdtable = {}
489 > command = cmdutil.command(cmdtable)
489 > command = cmdutil.command(cmdtable)
490 > """multirevs extension
490 > """multirevs extension
491 > Big multi-line module docstring."""
491 > Big multi-line module docstring."""
492 > @command('multirevs', [], 'ARG', norepo=True)
492 > @command('multirevs', [], 'ARG', norepo=True)
493 > def multirevs(ui, repo, arg, *args, **opts):
493 > def multirevs(ui, repo, arg, *args, **opts):
494 > """multirevs command"""
494 > """multirevs command"""
495 > pass
495 > pass
496 > EOF
496 > EOF
497 $ echo "multirevs = multirevs.py" >> $HGRCPATH
497 $ echo "multirevs = multirevs.py" >> $HGRCPATH
498
498
499 $ hg help multirevs
499 $ hg help multirevs
500 Specifying Multiple Revisions
500 Specifying Multiple Revisions
501 """""""""""""""""""""""""""""
501 """""""""""""""""""""""""""""
502
502
503 When Mercurial accepts more than one revision, they may be specified
503 When Mercurial accepts more than one revision, they may be specified
504 individually, or provided as a topologically continuous range, separated
504 individually, or provided as a topologically continuous range, separated
505 by the ":" character.
505 by the ":" character.
506
506
507 The syntax of range notation is [BEGIN]:[END], where BEGIN and END are
507 The syntax of range notation is [BEGIN]:[END], where BEGIN and END are
508 revision identifiers. Both BEGIN and END are optional. If BEGIN is not
508 revision identifiers. Both BEGIN and END are optional. If BEGIN is not
509 specified, it defaults to revision number 0. If END is not specified, it
509 specified, it defaults to revision number 0. If END is not specified, it
510 defaults to the tip. The range ":" thus means "all revisions".
510 defaults to the tip. The range ":" thus means "all revisions".
511
511
512 If BEGIN is greater than END, revisions are treated in reverse order.
512 If BEGIN is greater than END, revisions are treated in reverse order.
513
513
514 A range acts as a closed interval. This means that a range of 3:5 gives 3,
514 A range acts as a closed interval. This means that a range of 3:5 gives 3,
515 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6.
515 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6.
516
516
517 use "hg help -c multirevs" to see help for the multirevs command
517 use "hg help -c multirevs" to see help for the multirevs command
518
518
519
519
520
520
521
521
522
522
523
523
524 $ hg help -c multirevs
524 $ hg help -c multirevs
525 hg multirevs ARG
525 hg multirevs ARG
526
526
527 multirevs command
527 multirevs command
528
528
529 (some details hidden, use --verbose to show complete help)
529 (some details hidden, use --verbose to show complete help)
530
530
531
531
532
532
533 $ hg multirevs
533 $ hg multirevs
534 hg multirevs: invalid arguments
534 hg multirevs: invalid arguments
535 hg multirevs ARG
535 hg multirevs ARG
536
536
537 multirevs command
537 multirevs command
538
538
539 (use "hg multirevs -h" to show more help)
539 (use "hg multirevs -h" to show more help)
540 [255]
540 [255]
541
541
542
542
543
543
544 $ echo "multirevs = !" >> $HGRCPATH
544 $ echo "multirevs = !" >> $HGRCPATH
545
545
546 Issue811: Problem loading extensions twice (by site and by user)
546 Issue811: Problem loading extensions twice (by site and by user)
547
547
548 $ debugpath=`pwd`/debugissue811.py
548 $ debugpath=`pwd`/debugissue811.py
549 $ cat > debugissue811.py <<EOF
549 $ cat > debugissue811.py <<EOF
550 > '''show all loaded extensions
550 > '''show all loaded extensions
551 > '''
551 > '''
552 > from mercurial import cmdutil, commands, extensions
552 > from mercurial import cmdutil, commands, extensions
553 > cmdtable = {}
553 > cmdtable = {}
554 > command = cmdutil.command(cmdtable)
554 > command = cmdutil.command(cmdtable)
555 > @command('debugextensions', [], 'hg debugextensions', norepo=True)
555 > @command('debugextensions', [], 'hg debugextensions', norepo=True)
556 > def debugextensions(ui):
556 > def debugextensions(ui):
557 > "yet another debug command"
557 > "yet another debug command"
558 > ui.write("%s\n" % '\n'.join([x for x, y in extensions.extensions()]))
558 > ui.write("%s\n" % '\n'.join([x for x, y in extensions.extensions()]))
559 > EOF
559 > EOF
560 $ cat <<EOF >> $HGRCPATH
560 $ cat <<EOF >> $HGRCPATH
561 > debugissue811 = $debugpath
561 > debugissue811 = $debugpath
562 > mq =
562 > mq =
563 > strip =
563 > strip =
564 > hgext.mq =
564 > hgext.mq =
565 > hgext/mq =
565 > hgext/mq =
566 > EOF
566 > EOF
567
567
568 Show extensions:
568 Show extensions:
569 (note that mq force load strip, also checking it's not loaded twice)
569 (note that mq force load strip, also checking it's not loaded twice)
570
570
571 $ hg debugextensions
571 $ hg debugextensions
572 debugissue811
572 debugissue811
573 strip
573 strip
574 mq
574 mq
575
575
576 For extensions, which name matches one of its commands, help
576 For extensions, which name matches one of its commands, help
577 message should ask '-v -e' to get list of built-in aliases
577 message should ask '-v -e' to get list of built-in aliases
578 along with extension help itself
578 along with extension help itself
579
579
580 $ mkdir $TESTTMP/d
580 $ mkdir $TESTTMP/d
581 $ cat > $TESTTMP/d/dodo.py <<EOF
581 $ cat > $TESTTMP/d/dodo.py <<EOF
582 > """
582 > """
583 > This is an awesome 'dodo' extension. It does nothing and
583 > This is an awesome 'dodo' extension. It does nothing and
584 > writes 'Foo foo'
584 > writes 'Foo foo'
585 > """
585 > """
586 > from mercurial import cmdutil, commands
586 > from mercurial import cmdutil, commands
587 > cmdtable = {}
587 > cmdtable = {}
588 > command = cmdutil.command(cmdtable)
588 > command = cmdutil.command(cmdtable)
589 > @command('dodo', [], 'hg dodo')
589 > @command('dodo', [], 'hg dodo')
590 > def dodo(ui, *args, **kwargs):
590 > def dodo(ui, *args, **kwargs):
591 > """Does nothing"""
591 > """Does nothing"""
592 > ui.write("I do nothing. Yay\\n")
592 > ui.write("I do nothing. Yay\\n")
593 > @command('foofoo', [], 'hg foofoo')
593 > @command('foofoo', [], 'hg foofoo')
594 > def foofoo(ui, *args, **kwargs):
594 > def foofoo(ui, *args, **kwargs):
595 > """Writes 'Foo foo'"""
595 > """Writes 'Foo foo'"""
596 > ui.write("Foo foo\\n")
596 > ui.write("Foo foo\\n")
597 > EOF
597 > EOF
598 $ dodopath=$TESTTMP/d/dodo.py
598 $ dodopath=$TESTTMP/d/dodo.py
599
599
600 $ echo "dodo = $dodopath" >> $HGRCPATH
600 $ echo "dodo = $dodopath" >> $HGRCPATH
601
601
602 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
602 Make sure that user is asked to enter '-v -e' to get list of built-in aliases
603 $ hg help -e dodo
603 $ hg help -e dodo
604 dodo extension -
604 dodo extension -
605
605
606 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
606 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
607
607
608 list of commands:
608 list of commands:
609
609
610 dodo Does nothing
610 dodo Does nothing
611 foofoo Writes 'Foo foo'
611 foofoo Writes 'Foo foo'
612
612
613 (use "hg help -v -e dodo" to show built-in aliases and global options)
613 (use "hg help -v -e dodo" to show built-in aliases and global options)
614
614
615 Make sure that '-v -e' prints list of built-in aliases along with
615 Make sure that '-v -e' prints list of built-in aliases along with
616 extension help itself
616 extension help itself
617 $ hg help -v -e dodo
617 $ hg help -v -e dodo
618 dodo extension -
618 dodo extension -
619
619
620 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
620 This is an awesome 'dodo' extension. It does nothing and writes 'Foo foo'
621
621
622 list of commands:
622 list of commands:
623
623
624 dodo Does nothing
624 dodo Does nothing
625 foofoo Writes 'Foo foo'
625 foofoo Writes 'Foo foo'
626
626
627 global options ([+] can be repeated):
627 global options ([+] can be repeated):
628
628
629 -R --repository REPO repository root directory or name of overlay bundle
629 -R --repository REPO repository root directory or name of overlay bundle
630 file
630 file
631 --cwd DIR change working directory
631 --cwd DIR change working directory
632 -y --noninteractive do not prompt, automatically pick the first choice for
632 -y --noninteractive do not prompt, automatically pick the first choice for
633 all prompts
633 all prompts
634 -q --quiet suppress output
634 -q --quiet suppress output
635 -v --verbose enable additional output
635 -v --verbose enable additional output
636 --config CONFIG [+] set/override config option (use 'section.name=value')
636 --config CONFIG [+] set/override config option (use 'section.name=value')
637 --debug enable debugging output
637 --debug enable debugging output
638 --debugger start debugger
638 --debugger start debugger
639 --encoding ENCODE set the charset encoding (default: ascii)
639 --encoding ENCODE set the charset encoding (default: ascii)
640 --encodingmode MODE set the charset encoding mode (default: strict)
640 --encodingmode MODE set the charset encoding mode (default: strict)
641 --traceback always print a traceback on exception
641 --traceback always print a traceback on exception
642 --time time how long the command takes
642 --time time how long the command takes
643 --profile print command execution profile
643 --profile print command execution profile
644 --version output version information and exit
644 --version output version information and exit
645 -h --help display help and exit
645 -h --help display help and exit
646 --hidden consider hidden changesets
646 --hidden consider hidden changesets
647
647
648 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
648 Make sure that single '-v' option shows help and built-ins only for 'dodo' command
649 $ hg help -v dodo
649 $ hg help -v dodo
650 hg dodo
650 hg dodo
651
651
652 Does nothing
652 Does nothing
653
653
654 (use "hg help -e dodo" to show help for the dodo extension)
654 (use "hg help -e dodo" to show help for the dodo extension)
655
655
656 options:
656 options:
657
657
658 --mq operate on patch repository
658 --mq operate on patch repository
659
659
660 global options ([+] can be repeated):
660 global options ([+] can be repeated):
661
661
662 -R --repository REPO repository root directory or name of overlay bundle
662 -R --repository REPO repository root directory or name of overlay bundle
663 file
663 file
664 --cwd DIR change working directory
664 --cwd DIR change working directory
665 -y --noninteractive do not prompt, automatically pick the first choice for
665 -y --noninteractive do not prompt, automatically pick the first choice for
666 all prompts
666 all prompts
667 -q --quiet suppress output
667 -q --quiet suppress output
668 -v --verbose enable additional output
668 -v --verbose enable additional output
669 --config CONFIG [+] set/override config option (use 'section.name=value')
669 --config CONFIG [+] set/override config option (use 'section.name=value')
670 --debug enable debugging output
670 --debug enable debugging output
671 --debugger start debugger
671 --debugger start debugger
672 --encoding ENCODE set the charset encoding (default: ascii)
672 --encoding ENCODE set the charset encoding (default: ascii)
673 --encodingmode MODE set the charset encoding mode (default: strict)
673 --encodingmode MODE set the charset encoding mode (default: strict)
674 --traceback always print a traceback on exception
674 --traceback always print a traceback on exception
675 --time time how long the command takes
675 --time time how long the command takes
676 --profile print command execution profile
676 --profile print command execution profile
677 --version output version information and exit
677 --version output version information and exit
678 -h --help display help and exit
678 -h --help display help and exit
679 --hidden consider hidden changesets
679 --hidden consider hidden changesets
680
680
681 In case when extension name doesn't match any of its commands,
681 In case when extension name doesn't match any of its commands,
682 help message should ask for '-v' to get list of built-in aliases
682 help message should ask for '-v' to get list of built-in aliases
683 along with extension help
683 along with extension help
684 $ cat > $TESTTMP/d/dudu.py <<EOF
684 $ cat > $TESTTMP/d/dudu.py <<EOF
685 > """
685 > """
686 > This is an awesome 'dudu' extension. It does something and
686 > This is an awesome 'dudu' extension. It does something and
687 > also writes 'Beep beep'
687 > also writes 'Beep beep'
688 > """
688 > """
689 > from mercurial import cmdutil, commands
689 > from mercurial import cmdutil, commands
690 > cmdtable = {}
690 > cmdtable = {}
691 > command = cmdutil.command(cmdtable)
691 > command = cmdutil.command(cmdtable)
692 > @command('something', [], 'hg something')
692 > @command('something', [], 'hg something')
693 > def something(ui, *args, **kwargs):
693 > def something(ui, *args, **kwargs):
694 > """Does something"""
694 > """Does something"""
695 > ui.write("I do something. Yaaay\\n")
695 > ui.write("I do something. Yaaay\\n")
696 > @command('beep', [], 'hg beep')
696 > @command('beep', [], 'hg beep')
697 > def beep(ui, *args, **kwargs):
697 > def beep(ui, *args, **kwargs):
698 > """Writes 'Beep beep'"""
698 > """Writes 'Beep beep'"""
699 > ui.write("Beep beep\\n")
699 > ui.write("Beep beep\\n")
700 > EOF
700 > EOF
701 $ dudupath=$TESTTMP/d/dudu.py
701 $ dudupath=$TESTTMP/d/dudu.py
702
702
703 $ echo "dudu = $dudupath" >> $HGRCPATH
703 $ echo "dudu = $dudupath" >> $HGRCPATH
704
704
705 $ hg help -e dudu
705 $ hg help -e dudu
706 dudu extension -
706 dudu extension -
707
707
708 This is an awesome 'dudu' extension. It does something and also writes 'Beep
708 This is an awesome 'dudu' extension. It does something and also writes 'Beep
709 beep'
709 beep'
710
710
711 list of commands:
711 list of commands:
712
712
713 beep Writes 'Beep beep'
713 beep Writes 'Beep beep'
714 something Does something
714 something Does something
715
715
716 (use "hg help -v dudu" to show built-in aliases and global options)
716 (use "hg help -v dudu" to show built-in aliases and global options)
717
717
718 In case when extension name doesn't match any of its commands,
718 In case when extension name doesn't match any of its commands,
719 help options '-v' and '-v -e' should be equivalent
719 help options '-v' and '-v -e' should be equivalent
720 $ hg help -v dudu
720 $ hg help -v dudu
721 dudu extension -
721 dudu extension -
722
722
723 This is an awesome 'dudu' extension. It does something and also writes 'Beep
723 This is an awesome 'dudu' extension. It does something and also writes 'Beep
724 beep'
724 beep'
725
725
726 list of commands:
726 list of commands:
727
727
728 beep Writes 'Beep beep'
728 beep Writes 'Beep beep'
729 something Does something
729 something Does something
730
730
731 global options ([+] can be repeated):
731 global options ([+] can be repeated):
732
732
733 -R --repository REPO repository root directory or name of overlay bundle
733 -R --repository REPO repository root directory or name of overlay bundle
734 file
734 file
735 --cwd DIR change working directory
735 --cwd DIR change working directory
736 -y --noninteractive do not prompt, automatically pick the first choice for
736 -y --noninteractive do not prompt, automatically pick the first choice for
737 all prompts
737 all prompts
738 -q --quiet suppress output
738 -q --quiet suppress output
739 -v --verbose enable additional output
739 -v --verbose enable additional output
740 --config CONFIG [+] set/override config option (use 'section.name=value')
740 --config CONFIG [+] set/override config option (use 'section.name=value')
741 --debug enable debugging output
741 --debug enable debugging output
742 --debugger start debugger
742 --debugger start debugger
743 --encoding ENCODE set the charset encoding (default: ascii)
743 --encoding ENCODE set the charset encoding (default: ascii)
744 --encodingmode MODE set the charset encoding mode (default: strict)
744 --encodingmode MODE set the charset encoding mode (default: strict)
745 --traceback always print a traceback on exception
745 --traceback always print a traceback on exception
746 --time time how long the command takes
746 --time time how long the command takes
747 --profile print command execution profile
747 --profile print command execution profile
748 --version output version information and exit
748 --version output version information and exit
749 -h --help display help and exit
749 -h --help display help and exit
750 --hidden consider hidden changesets
750 --hidden consider hidden changesets
751
751
752 $ hg help -v -e dudu
752 $ hg help -v -e dudu
753 dudu extension -
753 dudu extension -
754
754
755 This is an awesome 'dudu' extension. It does something and also writes 'Beep
755 This is an awesome 'dudu' extension. It does something and also writes 'Beep
756 beep'
756 beep'
757
757
758 list of commands:
758 list of commands:
759
759
760 beep Writes 'Beep beep'
760 beep Writes 'Beep beep'
761 something Does something
761 something Does something
762
762
763 global options ([+] can be repeated):
763 global options ([+] can be repeated):
764
764
765 -R --repository REPO repository root directory or name of overlay bundle
765 -R --repository REPO repository root directory or name of overlay bundle
766 file
766 file
767 --cwd DIR change working directory
767 --cwd DIR change working directory
768 -y --noninteractive do not prompt, automatically pick the first choice for
768 -y --noninteractive do not prompt, automatically pick the first choice for
769 all prompts
769 all prompts
770 -q --quiet suppress output
770 -q --quiet suppress output
771 -v --verbose enable additional output
771 -v --verbose enable additional output
772 --config CONFIG [+] set/override config option (use 'section.name=value')
772 --config CONFIG [+] set/override config option (use 'section.name=value')
773 --debug enable debugging output
773 --debug enable debugging output
774 --debugger start debugger
774 --debugger start debugger
775 --encoding ENCODE set the charset encoding (default: ascii)
775 --encoding ENCODE set the charset encoding (default: ascii)
776 --encodingmode MODE set the charset encoding mode (default: strict)
776 --encodingmode MODE set the charset encoding mode (default: strict)
777 --traceback always print a traceback on exception
777 --traceback always print a traceback on exception
778 --time time how long the command takes
778 --time time how long the command takes
779 --profile print command execution profile
779 --profile print command execution profile
780 --version output version information and exit
780 --version output version information and exit
781 -h --help display help and exit
781 -h --help display help and exit
782 --hidden consider hidden changesets
782 --hidden consider hidden changesets
783
783
784 Disabled extension commands:
784 Disabled extension commands:
785
785
786 $ ORGHGRCPATH=$HGRCPATH
786 $ ORGHGRCPATH=$HGRCPATH
787 $ HGRCPATH=
787 $ HGRCPATH=
788 $ export HGRCPATH
788 $ export HGRCPATH
789 $ hg help email
789 $ hg help email
790 'email' is provided by the following extension:
790 'email' is provided by the following extension:
791
791
792 patchbomb command to send changesets as (a series of) patch emails
792 patchbomb command to send changesets as (a series of) patch emails
793
793
794 (use "hg help extensions" for information on enabling extensions)
794 (use "hg help extensions" for information on enabling extensions)
795
795
796
796
797 $ hg qdel
797 $ hg qdel
798 hg: unknown command 'qdel'
798 hg: unknown command 'qdel'
799 'qdelete' is provided by the following extension:
799 'qdelete' is provided by the following extension:
800
800
801 mq manage a stack of patches
801 mq manage a stack of patches
802
802
803 (use "hg help extensions" for information on enabling extensions)
803 (use "hg help extensions" for information on enabling extensions)
804 [255]
804 [255]
805
805
806
806
807 $ hg churn
807 $ hg churn
808 hg: unknown command 'churn'
808 hg: unknown command 'churn'
809 'churn' is provided by the following extension:
809 'churn' is provided by the following extension:
810
810
811 churn command to display statistics about repository history
811 churn command to display statistics about repository history
812
812
813 (use "hg help extensions" for information on enabling extensions)
813 (use "hg help extensions" for information on enabling extensions)
814 [255]
814 [255]
815
815
816
816
817
817
818 Disabled extensions:
818 Disabled extensions:
819
819
820 $ hg help churn
820 $ hg help churn
821 churn extension - command to display statistics about repository history
821 churn extension - command to display statistics about repository history
822
822
823 (use "hg help extensions" for information on enabling extensions)
823 (use "hg help extensions" for information on enabling extensions)
824
824
825 $ hg help patchbomb
825 $ hg help patchbomb
826 patchbomb extension - command to send changesets as (a series of) patch emails
826 patchbomb extension - command to send changesets as (a series of) patch emails
827
827
828 (use "hg help extensions" for information on enabling extensions)
828 (use "hg help extensions" for information on enabling extensions)
829
829
830
830
831 Broken disabled extension and command:
831 Broken disabled extension and command:
832
832
833 $ mkdir hgext
833 $ mkdir hgext
834 $ echo > hgext/__init__.py
834 $ echo > hgext/__init__.py
835 $ cat > hgext/broken.py <<EOF
835 $ cat > hgext/broken.py <<EOF
836 > "broken extension'
836 > "broken extension'
837 > EOF
837 > EOF
838 $ cat > path.py <<EOF
838 $ cat > path.py <<EOF
839 > import os, sys
839 > import os, sys
840 > sys.path.insert(0, os.environ['HGEXTPATH'])
840 > sys.path.insert(0, os.environ['HGEXTPATH'])
841 > EOF
841 > EOF
842 $ HGEXTPATH=`pwd`
842 $ HGEXTPATH=`pwd`
843 $ export HGEXTPATH
843 $ export HGEXTPATH
844
844
845 $ hg --config extensions.path=./path.py help broken
845 $ hg --config extensions.path=./path.py help broken
846 broken extension - (no help text available)
846 broken extension - (no help text available)
847
847
848 (use "hg help extensions" for information on enabling extensions)
848 (use "hg help extensions" for information on enabling extensions)
849
849
850
850
851 $ cat > hgext/forest.py <<EOF
851 $ cat > hgext/forest.py <<EOF
852 > cmdtable = None
852 > cmdtable = None
853 > EOF
853 > EOF
854 $ hg --config extensions.path=./path.py help foo > /dev/null
854 $ hg --config extensions.path=./path.py help foo > /dev/null
855 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
855 warning: error finding commands in $TESTTMP/hgext/forest.py (glob)
856 abort: no such help topic: foo
856 abort: no such help topic: foo
857 (try "hg help --keyword foo")
857 (try "hg help --keyword foo")
858 [255]
858 [255]
859
859
860 $ cat > throw.py <<EOF
860 $ cat > throw.py <<EOF
861 > from mercurial import cmdutil, commands
861 > from mercurial import cmdutil, commands, util
862 > cmdtable = {}
862 > cmdtable = {}
863 > command = cmdutil.command(cmdtable)
863 > command = cmdutil.command(cmdtable)
864 > class Bogon(Exception): pass
864 > class Bogon(Exception): pass
865 > @command('throw', [], 'hg throw', norepo=True)
865 > @command('throw', [], 'hg throw', norepo=True)
866 > def throw(ui, **opts):
866 > def throw(ui, **opts):
867 > """throws an exception"""
867 > """throws an exception"""
868 > raise Bogon()
868 > raise Bogon()
869 > EOF
869 > EOF
870
870
871 No declared supported version, extension complains:
871 No declared supported version, extension complains:
872 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
872 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
873 ** Unknown exception encountered with possibly-broken third-party extension throw
873 ** Unknown exception encountered with possibly-broken third-party extension throw
874 ** which supports versions unknown of Mercurial.
874 ** which supports versions unknown of Mercurial.
875 ** Please disable throw and try your action again.
875 ** Please disable throw and try your action again.
876 ** If that fixes the bug please report it to the extension author.
876 ** If that fixes the bug please report it to the extension author.
877 ** Python * (glob)
877 ** Python * (glob)
878 ** Mercurial Distributed SCM * (glob)
878 ** Mercurial Distributed SCM * (glob)
879 ** Extensions loaded: throw
879 ** Extensions loaded: throw
880
880
881 empty declaration of supported version, extension complains:
881 empty declaration of supported version, extension complains:
882 $ echo "testedwith = ''" >> throw.py
882 $ echo "testedwith = ''" >> throw.py
883 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
883 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
884 ** Unknown exception encountered with possibly-broken third-party extension throw
884 ** Unknown exception encountered with possibly-broken third-party extension throw
885 ** which supports versions unknown of Mercurial.
885 ** which supports versions unknown of Mercurial.
886 ** Please disable throw and try your action again.
886 ** Please disable throw and try your action again.
887 ** If that fixes the bug please report it to the extension author.
887 ** If that fixes the bug please report it to the extension author.
888 ** Python * (glob)
888 ** Python * (glob)
889 ** Mercurial Distributed SCM (*) (glob)
889 ** Mercurial Distributed SCM (*) (glob)
890 ** Extensions loaded: throw
890 ** Extensions loaded: throw
891
891
892 If the extension specifies a buglink, show that:
892 If the extension specifies a buglink, show that:
893 $ echo 'buglink = "http://example.com/bts"' >> throw.py
893 $ echo 'buglink = "http://example.com/bts"' >> throw.py
894 $ rm -f throw.pyc throw.pyo
894 $ rm -f throw.pyc throw.pyo
895 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
895 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
896 ** Unknown exception encountered with possibly-broken third-party extension throw
896 ** Unknown exception encountered with possibly-broken third-party extension throw
897 ** which supports versions unknown of Mercurial.
897 ** which supports versions unknown of Mercurial.
898 ** Please disable throw and try your action again.
898 ** Please disable throw and try your action again.
899 ** If that fixes the bug please report it to http://example.com/bts
899 ** If that fixes the bug please report it to http://example.com/bts
900 ** Python * (glob)
900 ** Python * (glob)
901 ** Mercurial Distributed SCM (*) (glob)
901 ** Mercurial Distributed SCM (*) (glob)
902 ** Extensions loaded: throw
902 ** Extensions loaded: throw
903
903
904 If the extensions declare outdated versions, accuse the older extension first:
904 If the extensions declare outdated versions, accuse the older extension first:
905 $ echo "from mercurial import util" >> older.py
905 $ echo "from mercurial import util" >> older.py
906 $ echo "util.version = lambda:'2.2'" >> older.py
906 $ echo "util.version = lambda:'2.2'" >> older.py
907 $ echo "testedwith = '1.9.3'" >> older.py
907 $ echo "testedwith = '1.9.3'" >> older.py
908 $ echo "testedwith = '2.1.1'" >> throw.py
908 $ echo "testedwith = '2.1.1'" >> throw.py
909 $ rm -f throw.pyc throw.pyo
909 $ rm -f throw.pyc throw.pyo
910 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
910 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
911 > throw 2>&1 | egrep '^\*\*'
911 > throw 2>&1 | egrep '^\*\*'
912 ** Unknown exception encountered with possibly-broken third-party extension older
912 ** Unknown exception encountered with possibly-broken third-party extension older
913 ** which supports versions 1.9.3 of Mercurial.
913 ** which supports versions 1.9 of Mercurial.
914 ** Please disable older and try your action again.
914 ** Please disable older and try your action again.
915 ** If that fixes the bug please report it to the extension author.
915 ** If that fixes the bug please report it to the extension author.
916 ** Python * (glob)
916 ** Python * (glob)
917 ** Mercurial Distributed SCM (version 2.2)
917 ** Mercurial Distributed SCM (version 2.2)
918 ** Extensions loaded: throw, older
918 ** Extensions loaded: throw, older
919
919
920 One extension only tested with older, one only with newer versions:
920 One extension only tested with older, one only with newer versions:
921 $ echo "util.version = lambda:'2.1'" >> older.py
921 $ echo "util.version = lambda:'2.1'" >> older.py
922 $ rm -f older.pyc older.pyo
922 $ rm -f older.pyc older.pyo
923 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
923 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
924 > throw 2>&1 | egrep '^\*\*'
924 > throw 2>&1 | egrep '^\*\*'
925 ** Unknown exception encountered with possibly-broken third-party extension older
925 ** Unknown exception encountered with possibly-broken third-party extension older
926 ** which supports versions 1.9.3 of Mercurial.
926 ** which supports versions 1.9 of Mercurial.
927 ** Please disable older and try your action again.
927 ** Please disable older and try your action again.
928 ** If that fixes the bug please report it to the extension author.
928 ** If that fixes the bug please report it to the extension author.
929 ** Python * (glob)
929 ** Python * (glob)
930 ** Mercurial Distributed SCM (version 2.1)
930 ** Mercurial Distributed SCM (version 2.1)
931 ** Extensions loaded: throw, older
931 ** Extensions loaded: throw, older
932
932
933 Older extension is tested with current version, the other only with newer:
933 Older extension is tested with current version, the other only with newer:
934 $ echo "util.version = lambda:'1.9.3'" >> older.py
934 $ echo "util.version = lambda:'1.9.3'" >> older.py
935 $ rm -f older.pyc older.pyo
935 $ rm -f older.pyc older.pyo
936 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
936 $ hg --config extensions.throw=throw.py --config extensions.older=older.py \
937 > throw 2>&1 | egrep '^\*\*'
937 > throw 2>&1 | egrep '^\*\*'
938 ** Unknown exception encountered with possibly-broken third-party extension throw
938 ** Unknown exception encountered with possibly-broken third-party extension throw
939 ** which supports versions 2.1.1 of Mercurial.
939 ** which supports versions 2.1 of Mercurial.
940 ** Please disable throw and try your action again.
940 ** Please disable throw and try your action again.
941 ** If that fixes the bug please report it to http://example.com/bts
941 ** If that fixes the bug please report it to http://example.com/bts
942 ** Python * (glob)
942 ** Python * (glob)
943 ** Mercurial Distributed SCM (version 1.9.3)
943 ** Mercurial Distributed SCM (version 1.9.3)
944 ** Extensions loaded: throw, older
944 ** Extensions loaded: throw, older
945
945
946 Declare the version as supporting this hg version, show regular bts link:
946 Declare the version as supporting this hg version, show regular bts link:
947 $ hgver=`$PYTHON -c 'from mercurial import util; print util.version().split("+")[0]'`
947 $ hgver=`$PYTHON -c 'from mercurial import util; print util.version().split("+")[0]'`
948 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
948 $ echo 'testedwith = """'"$hgver"'"""' >> throw.py
949 $ rm -f throw.pyc throw.pyo
949 $ rm -f throw.pyc throw.pyo
950 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
950 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
951 ** unknown exception encountered, please report by visiting
951 ** unknown exception encountered, please report by visiting
952 ** http://mercurial.selenic.com/wiki/BugTracker
952 ** http://mercurial.selenic.com/wiki/BugTracker
953 ** Python * (glob)
953 ** Python * (glob)
954 ** Mercurial Distributed SCM (*) (glob)
954 ** Mercurial Distributed SCM (*) (glob)
955 ** Extensions loaded: throw
955 ** Extensions loaded: throw
956
956
957 Patch version is ignored during compatibility check
958 $ echo "testedwith = '3.2'" >> throw.py
959 $ echo "util.version = lambda:'3.2.2'" >> throw.py
960 $ rm -f throw.pyc throw.pyo
961 $ hg --config extensions.throw=throw.py throw 2>&1 | egrep '^\*\*'
962 ** unknown exception encountered, please report by visiting
963 ** http://mercurial.selenic.com/wiki/BugTracker
964 ** Python * (glob)
965 ** Mercurial Distributed SCM (*) (glob)
966 ** Extensions loaded: throw
967
957 Test version number support in 'hg version':
968 Test version number support in 'hg version':
958 $ echo '__version__ = (1, 2, 3)' >> throw.py
969 $ echo '__version__ = (1, 2, 3)' >> throw.py
959 $ rm -f throw.pyc throw.pyo
970 $ rm -f throw.pyc throw.pyo
960 $ hg version -v
971 $ hg version -v
961 Mercurial Distributed SCM (version *) (glob)
972 Mercurial Distributed SCM (version *) (glob)
962 (see http://mercurial.selenic.com for more information)
973 (see http://mercurial.selenic.com for more information)
963
974
964 Copyright (C) 2005-* Matt Mackall and others (glob)
975 Copyright (C) 2005-* Matt Mackall and others (glob)
965 This is free software; see the source for copying conditions. There is NO
976 This is free software; see the source for copying conditions. There is NO
966 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
977 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
967
978
968 Enabled extensions:
979 Enabled extensions:
969
980
970
981
971 $ hg version -v --config extensions.throw=throw.py
982 $ hg version -v --config extensions.throw=throw.py
972 Mercurial Distributed SCM (version *) (glob)
983 Mercurial Distributed SCM (version *) (glob)
973 (see http://mercurial.selenic.com for more information)
984 (see http://mercurial.selenic.com for more information)
974
985
975 Copyright (C) 2005-* Matt Mackall and others (glob)
986 Copyright (C) 2005-* Matt Mackall and others (glob)
976 This is free software; see the source for copying conditions. There is NO
987 This is free software; see the source for copying conditions. There is NO
977 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
988 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
978
989
979 Enabled extensions:
990 Enabled extensions:
980
991
981 throw 1.2.3
992 throw 1.2.3
982 $ echo 'getversion = lambda: "1.twentythree"' >> throw.py
993 $ echo 'getversion = lambda: "1.twentythree"' >> throw.py
983 $ rm -f throw.pyc throw.pyo
994 $ rm -f throw.pyc throw.pyo
984 $ hg version -v --config extensions.throw=throw.py
995 $ hg version -v --config extensions.throw=throw.py
985 Mercurial Distributed SCM (version *) (glob)
996 Mercurial Distributed SCM (version *) (glob)
986 (see http://mercurial.selenic.com for more information)
997 (see http://mercurial.selenic.com for more information)
987
998
988 Copyright (C) 2005-* Matt Mackall and others (glob)
999 Copyright (C) 2005-* Matt Mackall and others (glob)
989 This is free software; see the source for copying conditions. There is NO
1000 This is free software; see the source for copying conditions. There is NO
990 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1001 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
991
1002
992 Enabled extensions:
1003 Enabled extensions:
993
1004
994 throw 1.twentythree
1005 throw 1.twentythree
995
1006
996 Restore HGRCPATH
1007 Restore HGRCPATH
997
1008
998 $ HGRCPATH=$ORGHGRCPATH
1009 $ HGRCPATH=$ORGHGRCPATH
999 $ export HGRCPATH
1010 $ export HGRCPATH
1000
1011
1001 Commands handling multiple repositories at a time should invoke only
1012 Commands handling multiple repositories at a time should invoke only
1002 "reposetup()" of extensions enabling in the target repository.
1013 "reposetup()" of extensions enabling in the target repository.
1003
1014
1004 $ mkdir reposetup-test
1015 $ mkdir reposetup-test
1005 $ cd reposetup-test
1016 $ cd reposetup-test
1006
1017
1007 $ cat > $TESTTMP/reposetuptest.py <<EOF
1018 $ cat > $TESTTMP/reposetuptest.py <<EOF
1008 > from mercurial import extensions
1019 > from mercurial import extensions
1009 > def reposetup(ui, repo):
1020 > def reposetup(ui, repo):
1010 > ui.write('reposetup() for %s\n' % (repo.root))
1021 > ui.write('reposetup() for %s\n' % (repo.root))
1011 > EOF
1022 > EOF
1012 $ hg init src
1023 $ hg init src
1013 $ echo a > src/a
1024 $ echo a > src/a
1014 $ hg -R src commit -Am '#0 at src/a'
1025 $ hg -R src commit -Am '#0 at src/a'
1015 adding a
1026 adding a
1016 $ echo '[extensions]' >> src/.hg/hgrc
1027 $ echo '[extensions]' >> src/.hg/hgrc
1017 $ echo '# enable extension locally' >> src/.hg/hgrc
1028 $ echo '# enable extension locally' >> src/.hg/hgrc
1018 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1029 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> src/.hg/hgrc
1019 $ hg -R src status
1030 $ hg -R src status
1020 reposetup() for $TESTTMP/reposetup-test/src (glob)
1031 reposetup() for $TESTTMP/reposetup-test/src (glob)
1021
1032
1022 $ hg clone -U src clone-dst1
1033 $ hg clone -U src clone-dst1
1023 reposetup() for $TESTTMP/reposetup-test/src (glob)
1034 reposetup() for $TESTTMP/reposetup-test/src (glob)
1024 $ hg init push-dst1
1035 $ hg init push-dst1
1025 $ hg -q -R src push push-dst1
1036 $ hg -q -R src push push-dst1
1026 reposetup() for $TESTTMP/reposetup-test/src (glob)
1037 reposetup() for $TESTTMP/reposetup-test/src (glob)
1027 $ hg init pull-src1
1038 $ hg init pull-src1
1028 $ hg -q -R pull-src1 pull src
1039 $ hg -q -R pull-src1 pull src
1029 reposetup() for $TESTTMP/reposetup-test/src (glob)
1040 reposetup() for $TESTTMP/reposetup-test/src (glob)
1030
1041
1031 $ cat <<EOF >> $HGRCPATH
1042 $ cat <<EOF >> $HGRCPATH
1032 > [extensions]
1043 > [extensions]
1033 > # disable extension globally and explicitly
1044 > # disable extension globally and explicitly
1034 > reposetuptest = !
1045 > reposetuptest = !
1035 > EOF
1046 > EOF
1036 $ hg clone -U src clone-dst2
1047 $ hg clone -U src clone-dst2
1037 reposetup() for $TESTTMP/reposetup-test/src (glob)
1048 reposetup() for $TESTTMP/reposetup-test/src (glob)
1038 $ hg init push-dst2
1049 $ hg init push-dst2
1039 $ hg -q -R src push push-dst2
1050 $ hg -q -R src push push-dst2
1040 reposetup() for $TESTTMP/reposetup-test/src (glob)
1051 reposetup() for $TESTTMP/reposetup-test/src (glob)
1041 $ hg init pull-src2
1052 $ hg init pull-src2
1042 $ hg -q -R pull-src2 pull src
1053 $ hg -q -R pull-src2 pull src
1043 reposetup() for $TESTTMP/reposetup-test/src (glob)
1054 reposetup() for $TESTTMP/reposetup-test/src (glob)
1044
1055
1045 $ cat <<EOF >> $HGRCPATH
1056 $ cat <<EOF >> $HGRCPATH
1046 > [extensions]
1057 > [extensions]
1047 > # enable extension globally
1058 > # enable extension globally
1048 > reposetuptest = $TESTTMP/reposetuptest.py
1059 > reposetuptest = $TESTTMP/reposetuptest.py
1049 > EOF
1060 > EOF
1050 $ hg clone -U src clone-dst3
1061 $ hg clone -U src clone-dst3
1051 reposetup() for $TESTTMP/reposetup-test/src (glob)
1062 reposetup() for $TESTTMP/reposetup-test/src (glob)
1052 reposetup() for $TESTTMP/reposetup-test/clone-dst3 (glob)
1063 reposetup() for $TESTTMP/reposetup-test/clone-dst3 (glob)
1053 $ hg init push-dst3
1064 $ hg init push-dst3
1054 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
1065 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
1055 $ hg -q -R src push push-dst3
1066 $ hg -q -R src push push-dst3
1056 reposetup() for $TESTTMP/reposetup-test/src (glob)
1067 reposetup() for $TESTTMP/reposetup-test/src (glob)
1057 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
1068 reposetup() for $TESTTMP/reposetup-test/push-dst3 (glob)
1058 $ hg init pull-src3
1069 $ hg init pull-src3
1059 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
1070 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
1060 $ hg -q -R pull-src3 pull src
1071 $ hg -q -R pull-src3 pull src
1061 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
1072 reposetup() for $TESTTMP/reposetup-test/pull-src3 (glob)
1062 reposetup() for $TESTTMP/reposetup-test/src (glob)
1073 reposetup() for $TESTTMP/reposetup-test/src (glob)
1063
1074
1064 $ echo '[extensions]' >> src/.hg/hgrc
1075 $ echo '[extensions]' >> src/.hg/hgrc
1065 $ echo '# disable extension locally' >> src/.hg/hgrc
1076 $ echo '# disable extension locally' >> src/.hg/hgrc
1066 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1077 $ echo 'reposetuptest = !' >> src/.hg/hgrc
1067 $ hg clone -U src clone-dst4
1078 $ hg clone -U src clone-dst4
1068 reposetup() for $TESTTMP/reposetup-test/clone-dst4 (glob)
1079 reposetup() for $TESTTMP/reposetup-test/clone-dst4 (glob)
1069 $ hg init push-dst4
1080 $ hg init push-dst4
1070 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
1081 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
1071 $ hg -q -R src push push-dst4
1082 $ hg -q -R src push push-dst4
1072 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
1083 reposetup() for $TESTTMP/reposetup-test/push-dst4 (glob)
1073 $ hg init pull-src4
1084 $ hg init pull-src4
1074 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
1085 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
1075 $ hg -q -R pull-src4 pull src
1086 $ hg -q -R pull-src4 pull src
1076 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
1087 reposetup() for $TESTTMP/reposetup-test/pull-src4 (glob)
1077
1088
1078 disabling in command line overlays with all configuration
1089 disabling in command line overlays with all configuration
1079 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1090 $ hg --config extensions.reposetuptest=! clone -U src clone-dst5
1080 $ hg --config extensions.reposetuptest=! init push-dst5
1091 $ hg --config extensions.reposetuptest=! init push-dst5
1081 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1092 $ hg --config extensions.reposetuptest=! -q -R src push push-dst5
1082 $ hg --config extensions.reposetuptest=! init pull-src5
1093 $ hg --config extensions.reposetuptest=! init pull-src5
1083 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1094 $ hg --config extensions.reposetuptest=! -q -R pull-src5 pull src
1084
1095
1085 $ cat <<EOF >> $HGRCPATH
1096 $ cat <<EOF >> $HGRCPATH
1086 > [extensions]
1097 > [extensions]
1087 > # disable extension globally and explicitly
1098 > # disable extension globally and explicitly
1088 > reposetuptest = !
1099 > reposetuptest = !
1089 > EOF
1100 > EOF
1090 $ hg init parent
1101 $ hg init parent
1091 $ hg init parent/sub1
1102 $ hg init parent/sub1
1092 $ echo 1 > parent/sub1/1
1103 $ echo 1 > parent/sub1/1
1093 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1104 $ hg -R parent/sub1 commit -Am '#0 at parent/sub1'
1094 adding 1
1105 adding 1
1095 $ hg init parent/sub2
1106 $ hg init parent/sub2
1096 $ hg init parent/sub2/sub21
1107 $ hg init parent/sub2/sub21
1097 $ echo 21 > parent/sub2/sub21/21
1108 $ echo 21 > parent/sub2/sub21/21
1098 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1109 $ hg -R parent/sub2/sub21 commit -Am '#0 at parent/sub2/sub21'
1099 adding 21
1110 adding 21
1100 $ cat > parent/sub2/.hgsub <<EOF
1111 $ cat > parent/sub2/.hgsub <<EOF
1101 > sub21 = sub21
1112 > sub21 = sub21
1102 > EOF
1113 > EOF
1103 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1114 $ hg -R parent/sub2 commit -Am '#0 at parent/sub2'
1104 adding .hgsub
1115 adding .hgsub
1105 $ hg init parent/sub3
1116 $ hg init parent/sub3
1106 $ echo 3 > parent/sub3/3
1117 $ echo 3 > parent/sub3/3
1107 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1118 $ hg -R parent/sub3 commit -Am '#0 at parent/sub3'
1108 adding 3
1119 adding 3
1109 $ cat > parent/.hgsub <<EOF
1120 $ cat > parent/.hgsub <<EOF
1110 > sub1 = sub1
1121 > sub1 = sub1
1111 > sub2 = sub2
1122 > sub2 = sub2
1112 > sub3 = sub3
1123 > sub3 = sub3
1113 > EOF
1124 > EOF
1114 $ hg -R parent commit -Am '#0 at parent'
1125 $ hg -R parent commit -Am '#0 at parent'
1115 adding .hgsub
1126 adding .hgsub
1116 $ echo '[extensions]' >> parent/.hg/hgrc
1127 $ echo '[extensions]' >> parent/.hg/hgrc
1117 $ echo '# enable extension locally' >> parent/.hg/hgrc
1128 $ echo '# enable extension locally' >> parent/.hg/hgrc
1118 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1129 $ echo "reposetuptest = $TESTTMP/reposetuptest.py" >> parent/.hg/hgrc
1119 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1130 $ cp parent/.hg/hgrc parent/sub2/.hg/hgrc
1120 $ hg -R parent status -S -A
1131 $ hg -R parent status -S -A
1121 reposetup() for $TESTTMP/reposetup-test/parent (glob)
1132 reposetup() for $TESTTMP/reposetup-test/parent (glob)
1122 reposetup() for $TESTTMP/reposetup-test/parent/sub2 (glob)
1133 reposetup() for $TESTTMP/reposetup-test/parent/sub2 (glob)
1123 C .hgsub
1134 C .hgsub
1124 C .hgsubstate
1135 C .hgsubstate
1125 C sub1/1
1136 C sub1/1
1126 C sub2/.hgsub
1137 C sub2/.hgsub
1127 C sub2/.hgsubstate
1138 C sub2/.hgsubstate
1128 C sub2/sub21/21
1139 C sub2/sub21/21
1129 C sub3/3
1140 C sub3/3
1130
1141
1131 $ cd ..
1142 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now