##// END OF EJS Templates
clfilter: enforce hidden filtering on all repository accesses...
Pierre-Yves David -
r18303:4d1671b3 default
parent child Browse files
Show More
@@ -1,835 +1,833 b''
1 # dispatch.py - command dispatching for mercurial
1 # dispatch.py - command dispatching for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
10 import util, commands, hg, fancyopts, extensions, hook, error
10 import util, commands, hg, fancyopts, extensions, hook, error
11 import cmdutil, encoding
11 import cmdutil, encoding
12 import ui as uimod
12 import ui as uimod
13
13
14 class request(object):
14 class request(object):
15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
16 ferr=None):
16 ferr=None):
17 self.args = args
17 self.args = args
18 self.ui = ui
18 self.ui = ui
19 self.repo = repo
19 self.repo = repo
20
20
21 # input/output/error streams
21 # input/output/error streams
22 self.fin = fin
22 self.fin = fin
23 self.fout = fout
23 self.fout = fout
24 self.ferr = ferr
24 self.ferr = ferr
25
25
26 def run():
26 def run():
27 "run the command in sys.argv"
27 "run the command in sys.argv"
28 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
28 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
29
29
30 def dispatch(req):
30 def dispatch(req):
31 "run the command specified in req.args"
31 "run the command specified in req.args"
32 if req.ferr:
32 if req.ferr:
33 ferr = req.ferr
33 ferr = req.ferr
34 elif req.ui:
34 elif req.ui:
35 ferr = req.ui.ferr
35 ferr = req.ui.ferr
36 else:
36 else:
37 ferr = sys.stderr
37 ferr = sys.stderr
38
38
39 try:
39 try:
40 if not req.ui:
40 if not req.ui:
41 req.ui = uimod.ui()
41 req.ui = uimod.ui()
42 if '--traceback' in req.args:
42 if '--traceback' in req.args:
43 req.ui.setconfig('ui', 'traceback', 'on')
43 req.ui.setconfig('ui', 'traceback', 'on')
44
44
45 # set ui streams from the request
45 # set ui streams from the request
46 if req.fin:
46 if req.fin:
47 req.ui.fin = req.fin
47 req.ui.fin = req.fin
48 if req.fout:
48 if req.fout:
49 req.ui.fout = req.fout
49 req.ui.fout = req.fout
50 if req.ferr:
50 if req.ferr:
51 req.ui.ferr = req.ferr
51 req.ui.ferr = req.ferr
52 except util.Abort, inst:
52 except util.Abort, inst:
53 ferr.write(_("abort: %s\n") % inst)
53 ferr.write(_("abort: %s\n") % inst)
54 if inst.hint:
54 if inst.hint:
55 ferr.write(_("(%s)\n") % inst.hint)
55 ferr.write(_("(%s)\n") % inst.hint)
56 return -1
56 return -1
57 except error.ParseError, inst:
57 except error.ParseError, inst:
58 if len(inst.args) > 1:
58 if len(inst.args) > 1:
59 ferr.write(_("hg: parse error at %s: %s\n") %
59 ferr.write(_("hg: parse error at %s: %s\n") %
60 (inst.args[1], inst.args[0]))
60 (inst.args[1], inst.args[0]))
61 else:
61 else:
62 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
62 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
63 return -1
63 return -1
64
64
65 return _runcatch(req)
65 return _runcatch(req)
66
66
67 def _runcatch(req):
67 def _runcatch(req):
68 def catchterm(*args):
68 def catchterm(*args):
69 raise error.SignalInterrupt
69 raise error.SignalInterrupt
70
70
71 ui = req.ui
71 ui = req.ui
72 try:
72 try:
73 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
73 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
74 num = getattr(signal, name, None)
74 num = getattr(signal, name, None)
75 if num:
75 if num:
76 signal.signal(num, catchterm)
76 signal.signal(num, catchterm)
77 except ValueError:
77 except ValueError:
78 pass # happens if called in a thread
78 pass # happens if called in a thread
79
79
80 try:
80 try:
81 try:
81 try:
82 # enter the debugger before command execution
82 # enter the debugger before command execution
83 if '--debugger' in req.args:
83 if '--debugger' in req.args:
84 ui.warn(_("entering debugger - "
84 ui.warn(_("entering debugger - "
85 "type c to continue starting hg or h for help\n"))
85 "type c to continue starting hg or h for help\n"))
86 pdb.set_trace()
86 pdb.set_trace()
87 try:
87 try:
88 return _dispatch(req)
88 return _dispatch(req)
89 finally:
89 finally:
90 ui.flush()
90 ui.flush()
91 except: # re-raises
91 except: # re-raises
92 # enter the debugger when we hit an exception
92 # enter the debugger when we hit an exception
93 if '--debugger' in req.args:
93 if '--debugger' in req.args:
94 traceback.print_exc()
94 traceback.print_exc()
95 pdb.post_mortem(sys.exc_info()[2])
95 pdb.post_mortem(sys.exc_info()[2])
96 ui.traceback()
96 ui.traceback()
97 raise
97 raise
98
98
99 # Global exception handling, alphabetically
99 # Global exception handling, alphabetically
100 # Mercurial-specific first, followed by built-in and library exceptions
100 # Mercurial-specific first, followed by built-in and library exceptions
101 except error.AmbiguousCommand, inst:
101 except error.AmbiguousCommand, inst:
102 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
102 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
103 (inst.args[0], " ".join(inst.args[1])))
103 (inst.args[0], " ".join(inst.args[1])))
104 except error.ParseError, inst:
104 except error.ParseError, inst:
105 if len(inst.args) > 1:
105 if len(inst.args) > 1:
106 ui.warn(_("hg: parse error at %s: %s\n") %
106 ui.warn(_("hg: parse error at %s: %s\n") %
107 (inst.args[1], inst.args[0]))
107 (inst.args[1], inst.args[0]))
108 else:
108 else:
109 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
109 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
110 return -1
110 return -1
111 except error.LockHeld, inst:
111 except error.LockHeld, inst:
112 if inst.errno == errno.ETIMEDOUT:
112 if inst.errno == errno.ETIMEDOUT:
113 reason = _('timed out waiting for lock held by %s') % inst.locker
113 reason = _('timed out waiting for lock held by %s') % inst.locker
114 else:
114 else:
115 reason = _('lock held by %s') % inst.locker
115 reason = _('lock held by %s') % inst.locker
116 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
116 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
117 except error.LockUnavailable, inst:
117 except error.LockUnavailable, inst:
118 ui.warn(_("abort: could not lock %s: %s\n") %
118 ui.warn(_("abort: could not lock %s: %s\n") %
119 (inst.desc or inst.filename, inst.strerror))
119 (inst.desc or inst.filename, inst.strerror))
120 except error.CommandError, inst:
120 except error.CommandError, inst:
121 if inst.args[0]:
121 if inst.args[0]:
122 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
122 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
123 commands.help_(ui, inst.args[0], full=False, command=True)
123 commands.help_(ui, inst.args[0], full=False, command=True)
124 else:
124 else:
125 ui.warn(_("hg: %s\n") % inst.args[1])
125 ui.warn(_("hg: %s\n") % inst.args[1])
126 commands.help_(ui, 'shortlist')
126 commands.help_(ui, 'shortlist')
127 except error.OutOfBandError, inst:
127 except error.OutOfBandError, inst:
128 ui.warn(_("abort: remote error:\n"))
128 ui.warn(_("abort: remote error:\n"))
129 ui.warn(''.join(inst.args))
129 ui.warn(''.join(inst.args))
130 except error.RepoError, inst:
130 except error.RepoError, inst:
131 ui.warn(_("abort: %s!\n") % inst)
131 ui.warn(_("abort: %s!\n") % inst)
132 if inst.hint:
132 if inst.hint:
133 ui.warn(_("(%s)\n") % inst.hint)
133 ui.warn(_("(%s)\n") % inst.hint)
134 except error.ResponseError, inst:
134 except error.ResponseError, inst:
135 ui.warn(_("abort: %s") % inst.args[0])
135 ui.warn(_("abort: %s") % inst.args[0])
136 if not isinstance(inst.args[1], basestring):
136 if not isinstance(inst.args[1], basestring):
137 ui.warn(" %r\n" % (inst.args[1],))
137 ui.warn(" %r\n" % (inst.args[1],))
138 elif not inst.args[1]:
138 elif not inst.args[1]:
139 ui.warn(_(" empty string\n"))
139 ui.warn(_(" empty string\n"))
140 else:
140 else:
141 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
141 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
142 except error.RevlogError, inst:
142 except error.RevlogError, inst:
143 ui.warn(_("abort: %s!\n") % inst)
143 ui.warn(_("abort: %s!\n") % inst)
144 except error.SignalInterrupt:
144 except error.SignalInterrupt:
145 ui.warn(_("killed!\n"))
145 ui.warn(_("killed!\n"))
146 except error.UnknownCommand, inst:
146 except error.UnknownCommand, inst:
147 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
147 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
148 try:
148 try:
149 # check if the command is in a disabled extension
149 # check if the command is in a disabled extension
150 # (but don't check for extensions themselves)
150 # (but don't check for extensions themselves)
151 commands.help_(ui, inst.args[0], unknowncmd=True)
151 commands.help_(ui, inst.args[0], unknowncmd=True)
152 except error.UnknownCommand:
152 except error.UnknownCommand:
153 commands.help_(ui, 'shortlist')
153 commands.help_(ui, 'shortlist')
154 except util.Abort, inst:
154 except util.Abort, inst:
155 ui.warn(_("abort: %s\n") % inst)
155 ui.warn(_("abort: %s\n") % inst)
156 if inst.hint:
156 if inst.hint:
157 ui.warn(_("(%s)\n") % inst.hint)
157 ui.warn(_("(%s)\n") % inst.hint)
158 except ImportError, inst:
158 except ImportError, inst:
159 ui.warn(_("abort: %s!\n") % inst)
159 ui.warn(_("abort: %s!\n") % inst)
160 m = str(inst).split()[-1]
160 m = str(inst).split()[-1]
161 if m in "mpatch bdiff".split():
161 if m in "mpatch bdiff".split():
162 ui.warn(_("(did you forget to compile extensions?)\n"))
162 ui.warn(_("(did you forget to compile extensions?)\n"))
163 elif m in "zlib".split():
163 elif m in "zlib".split():
164 ui.warn(_("(is your Python install correct?)\n"))
164 ui.warn(_("(is your Python install correct?)\n"))
165 except IOError, inst:
165 except IOError, inst:
166 if util.safehasattr(inst, "code"):
166 if util.safehasattr(inst, "code"):
167 ui.warn(_("abort: %s\n") % inst)
167 ui.warn(_("abort: %s\n") % inst)
168 elif util.safehasattr(inst, "reason"):
168 elif util.safehasattr(inst, "reason"):
169 try: # usually it is in the form (errno, strerror)
169 try: # usually it is in the form (errno, strerror)
170 reason = inst.reason.args[1]
170 reason = inst.reason.args[1]
171 except (AttributeError, IndexError):
171 except (AttributeError, IndexError):
172 # it might be anything, for example a string
172 # it might be anything, for example a string
173 reason = inst.reason
173 reason = inst.reason
174 ui.warn(_("abort: error: %s\n") % reason)
174 ui.warn(_("abort: error: %s\n") % reason)
175 elif util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
175 elif util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
176 if ui.debugflag:
176 if ui.debugflag:
177 ui.warn(_("broken pipe\n"))
177 ui.warn(_("broken pipe\n"))
178 elif getattr(inst, "strerror", None):
178 elif getattr(inst, "strerror", None):
179 if getattr(inst, "filename", None):
179 if getattr(inst, "filename", None):
180 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
180 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
181 else:
181 else:
182 ui.warn(_("abort: %s\n") % inst.strerror)
182 ui.warn(_("abort: %s\n") % inst.strerror)
183 else:
183 else:
184 raise
184 raise
185 except OSError, inst:
185 except OSError, inst:
186 if getattr(inst, "filename", None) is not None:
186 if getattr(inst, "filename", None) is not None:
187 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
187 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
188 else:
188 else:
189 ui.warn(_("abort: %s\n") % inst.strerror)
189 ui.warn(_("abort: %s\n") % inst.strerror)
190 except KeyboardInterrupt:
190 except KeyboardInterrupt:
191 try:
191 try:
192 ui.warn(_("interrupted!\n"))
192 ui.warn(_("interrupted!\n"))
193 except IOError, inst:
193 except IOError, inst:
194 if inst.errno == errno.EPIPE:
194 if inst.errno == errno.EPIPE:
195 if ui.debugflag:
195 if ui.debugflag:
196 ui.warn(_("\nbroken pipe\n"))
196 ui.warn(_("\nbroken pipe\n"))
197 else:
197 else:
198 raise
198 raise
199 except MemoryError:
199 except MemoryError:
200 ui.warn(_("abort: out of memory\n"))
200 ui.warn(_("abort: out of memory\n"))
201 except SystemExit, inst:
201 except SystemExit, inst:
202 # Commands shouldn't sys.exit directly, but give a return code.
202 # Commands shouldn't sys.exit directly, but give a return code.
203 # Just in case catch this and and pass exit code to caller.
203 # Just in case catch this and and pass exit code to caller.
204 return inst.code
204 return inst.code
205 except socket.error, inst:
205 except socket.error, inst:
206 ui.warn(_("abort: %s\n") % inst.args[-1])
206 ui.warn(_("abort: %s\n") % inst.args[-1])
207 except: # re-raises
207 except: # re-raises
208 myver = util.version()
208 myver = util.version()
209 # For compatibility checking, we discard the portion of the hg
209 # For compatibility checking, we discard the portion of the hg
210 # version after the + on the assumption that if a "normal
210 # version after the + on the assumption that if a "normal
211 # user" is running a build with a + in it the packager
211 # user" is running a build with a + in it the packager
212 # probably built from fairly close to a tag and anyone with a
212 # probably built from fairly close to a tag and anyone with a
213 # 'make local' copy of hg (where the version number can be out
213 # 'make local' copy of hg (where the version number can be out
214 # of date) will be clueful enough to notice the implausible
214 # of date) will be clueful enough to notice the implausible
215 # version number and try updating.
215 # version number and try updating.
216 compare = myver.split('+')[0]
216 compare = myver.split('+')[0]
217 ct = tuplever(compare)
217 ct = tuplever(compare)
218 worst = None, ct, ''
218 worst = None, ct, ''
219 for name, mod in extensions.extensions():
219 for name, mod in extensions.extensions():
220 testedwith = getattr(mod, 'testedwith', '')
220 testedwith = getattr(mod, 'testedwith', '')
221 report = getattr(mod, 'buglink', _('the extension author.'))
221 report = getattr(mod, 'buglink', _('the extension author.'))
222 if not testedwith.strip():
222 if not testedwith.strip():
223 # We found an untested extension. It's likely the culprit.
223 # We found an untested extension. It's likely the culprit.
224 worst = name, 'unknown', report
224 worst = name, 'unknown', report
225 break
225 break
226 if compare not in testedwith.split() and testedwith != 'internal':
226 if compare not in testedwith.split() and testedwith != 'internal':
227 tested = [tuplever(v) for v in testedwith.split()]
227 tested = [tuplever(v) for v in testedwith.split()]
228 lower = [t for t in tested if t < ct]
228 lower = [t for t in tested if t < ct]
229 nearest = max(lower or tested)
229 nearest = max(lower or tested)
230 if worst[0] is None or nearest < worst[1]:
230 if worst[0] is None or nearest < worst[1]:
231 worst = name, nearest, report
231 worst = name, nearest, report
232 if worst[0] is not None:
232 if worst[0] is not None:
233 name, testedwith, report = worst
233 name, testedwith, report = worst
234 if not isinstance(testedwith, str):
234 if not isinstance(testedwith, str):
235 testedwith = '.'.join([str(c) for c in testedwith])
235 testedwith = '.'.join([str(c) for c in testedwith])
236 warning = (_('** Unknown exception encountered with '
236 warning = (_('** Unknown exception encountered with '
237 'possibly-broken third-party extension %s\n'
237 'possibly-broken third-party extension %s\n'
238 '** which supports versions %s of Mercurial.\n'
238 '** which supports versions %s of Mercurial.\n'
239 '** Please disable %s and try your action again.\n'
239 '** Please disable %s and try your action again.\n'
240 '** If that fixes the bug please report it to %s\n')
240 '** If that fixes the bug please report it to %s\n')
241 % (name, testedwith, name, report))
241 % (name, testedwith, name, report))
242 else:
242 else:
243 warning = (_("** unknown exception encountered, "
243 warning = (_("** unknown exception encountered, "
244 "please report by visiting\n") +
244 "please report by visiting\n") +
245 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
245 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
246 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
246 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
247 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
247 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
248 (_("** Extensions loaded: %s\n") %
248 (_("** Extensions loaded: %s\n") %
249 ", ".join([x[0] for x in extensions.extensions()])))
249 ", ".join([x[0] for x in extensions.extensions()])))
250 ui.warn(warning)
250 ui.warn(warning)
251 raise
251 raise
252
252
253 return -1
253 return -1
254
254
255 def tuplever(v):
255 def tuplever(v):
256 try:
256 try:
257 return tuple([int(i) for i in v.split('.')])
257 return tuple([int(i) for i in v.split('.')])
258 except ValueError:
258 except ValueError:
259 return tuple()
259 return tuple()
260
260
261 def aliasargs(fn, givenargs):
261 def aliasargs(fn, givenargs):
262 args = getattr(fn, 'args', [])
262 args = getattr(fn, 'args', [])
263 if args:
263 if args:
264 cmd = ' '.join(map(util.shellquote, args))
264 cmd = ' '.join(map(util.shellquote, args))
265
265
266 nums = []
266 nums = []
267 def replacer(m):
267 def replacer(m):
268 num = int(m.group(1)) - 1
268 num = int(m.group(1)) - 1
269 nums.append(num)
269 nums.append(num)
270 if num < len(givenargs):
270 if num < len(givenargs):
271 return givenargs[num]
271 return givenargs[num]
272 raise util.Abort(_('too few arguments for command alias'))
272 raise util.Abort(_('too few arguments for command alias'))
273 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
273 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
274 givenargs = [x for i, x in enumerate(givenargs)
274 givenargs = [x for i, x in enumerate(givenargs)
275 if i not in nums]
275 if i not in nums]
276 args = shlex.split(cmd)
276 args = shlex.split(cmd)
277 return args + givenargs
277 return args + givenargs
278
278
279 class cmdalias(object):
279 class cmdalias(object):
280 def __init__(self, name, definition, cmdtable):
280 def __init__(self, name, definition, cmdtable):
281 self.name = self.cmd = name
281 self.name = self.cmd = name
282 self.cmdname = ''
282 self.cmdname = ''
283 self.definition = definition
283 self.definition = definition
284 self.args = []
284 self.args = []
285 self.opts = []
285 self.opts = []
286 self.help = ''
286 self.help = ''
287 self.norepo = True
287 self.norepo = True
288 self.optionalrepo = False
288 self.optionalrepo = False
289 self.badalias = False
289 self.badalias = False
290
290
291 try:
291 try:
292 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
292 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
293 for alias, e in cmdtable.iteritems():
293 for alias, e in cmdtable.iteritems():
294 if e is entry:
294 if e is entry:
295 self.cmd = alias
295 self.cmd = alias
296 break
296 break
297 self.shadows = True
297 self.shadows = True
298 except error.UnknownCommand:
298 except error.UnknownCommand:
299 self.shadows = False
299 self.shadows = False
300
300
301 if not self.definition:
301 if not self.definition:
302 def fn(ui, *args):
302 def fn(ui, *args):
303 ui.warn(_("no definition for alias '%s'\n") % self.name)
303 ui.warn(_("no definition for alias '%s'\n") % self.name)
304 return 1
304 return 1
305 self.fn = fn
305 self.fn = fn
306 self.badalias = True
306 self.badalias = True
307 return
307 return
308
308
309 if self.definition.startswith('!'):
309 if self.definition.startswith('!'):
310 self.shell = True
310 self.shell = True
311 def fn(ui, *args):
311 def fn(ui, *args):
312 env = {'HG_ARGS': ' '.join((self.name,) + args)}
312 env = {'HG_ARGS': ' '.join((self.name,) + args)}
313 def _checkvar(m):
313 def _checkvar(m):
314 if m.groups()[0] == '$':
314 if m.groups()[0] == '$':
315 return m.group()
315 return m.group()
316 elif int(m.groups()[0]) <= len(args):
316 elif int(m.groups()[0]) <= len(args):
317 return m.group()
317 return m.group()
318 else:
318 else:
319 ui.debug("No argument found for substitution "
319 ui.debug("No argument found for substitution "
320 "of %i variable in alias '%s' definition."
320 "of %i variable in alias '%s' definition."
321 % (int(m.groups()[0]), self.name))
321 % (int(m.groups()[0]), self.name))
322 return ''
322 return ''
323 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
323 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
324 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
324 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
325 replace['0'] = self.name
325 replace['0'] = self.name
326 replace['@'] = ' '.join(args)
326 replace['@'] = ' '.join(args)
327 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
327 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
328 return util.system(cmd, environ=env, out=ui.fout)
328 return util.system(cmd, environ=env, out=ui.fout)
329 self.fn = fn
329 self.fn = fn
330 return
330 return
331
331
332 args = shlex.split(self.definition)
332 args = shlex.split(self.definition)
333 self.cmdname = cmd = args.pop(0)
333 self.cmdname = cmd = args.pop(0)
334 args = map(util.expandpath, args)
334 args = map(util.expandpath, args)
335
335
336 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
336 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
337 if _earlygetopt([invalidarg], args):
337 if _earlygetopt([invalidarg], args):
338 def fn(ui, *args):
338 def fn(ui, *args):
339 ui.warn(_("error in definition for alias '%s': %s may only "
339 ui.warn(_("error in definition for alias '%s': %s may only "
340 "be given on the command line\n")
340 "be given on the command line\n")
341 % (self.name, invalidarg))
341 % (self.name, invalidarg))
342 return 1
342 return 1
343
343
344 self.fn = fn
344 self.fn = fn
345 self.badalias = True
345 self.badalias = True
346 return
346 return
347
347
348 try:
348 try:
349 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
349 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
350 if len(tableentry) > 2:
350 if len(tableentry) > 2:
351 self.fn, self.opts, self.help = tableentry
351 self.fn, self.opts, self.help = tableentry
352 else:
352 else:
353 self.fn, self.opts = tableentry
353 self.fn, self.opts = tableentry
354
354
355 self.args = aliasargs(self.fn, args)
355 self.args = aliasargs(self.fn, args)
356 if cmd not in commands.norepo.split(' '):
356 if cmd not in commands.norepo.split(' '):
357 self.norepo = False
357 self.norepo = False
358 if cmd in commands.optionalrepo.split(' '):
358 if cmd in commands.optionalrepo.split(' '):
359 self.optionalrepo = True
359 self.optionalrepo = True
360 if self.help.startswith("hg " + cmd):
360 if self.help.startswith("hg " + cmd):
361 # drop prefix in old-style help lines so hg shows the alias
361 # drop prefix in old-style help lines so hg shows the alias
362 self.help = self.help[4 + len(cmd):]
362 self.help = self.help[4 + len(cmd):]
363 self.__doc__ = self.fn.__doc__
363 self.__doc__ = self.fn.__doc__
364
364
365 except error.UnknownCommand:
365 except error.UnknownCommand:
366 def fn(ui, *args):
366 def fn(ui, *args):
367 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
367 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
368 % (self.name, cmd))
368 % (self.name, cmd))
369 try:
369 try:
370 # check if the command is in a disabled extension
370 # check if the command is in a disabled extension
371 commands.help_(ui, cmd, unknowncmd=True)
371 commands.help_(ui, cmd, unknowncmd=True)
372 except error.UnknownCommand:
372 except error.UnknownCommand:
373 pass
373 pass
374 return 1
374 return 1
375 self.fn = fn
375 self.fn = fn
376 self.badalias = True
376 self.badalias = True
377 except error.AmbiguousCommand:
377 except error.AmbiguousCommand:
378 def fn(ui, *args):
378 def fn(ui, *args):
379 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
379 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
380 % (self.name, cmd))
380 % (self.name, cmd))
381 return 1
381 return 1
382 self.fn = fn
382 self.fn = fn
383 self.badalias = True
383 self.badalias = True
384
384
385 def __call__(self, ui, *args, **opts):
385 def __call__(self, ui, *args, **opts):
386 if self.shadows:
386 if self.shadows:
387 ui.debug("alias '%s' shadows command '%s'\n" %
387 ui.debug("alias '%s' shadows command '%s'\n" %
388 (self.name, self.cmdname))
388 (self.name, self.cmdname))
389
389
390 if util.safehasattr(self, 'shell'):
390 if util.safehasattr(self, 'shell'):
391 return self.fn(ui, *args, **opts)
391 return self.fn(ui, *args, **opts)
392 else:
392 else:
393 try:
393 try:
394 util.checksignature(self.fn)(ui, *args, **opts)
394 util.checksignature(self.fn)(ui, *args, **opts)
395 except error.SignatureError:
395 except error.SignatureError:
396 args = ' '.join([self.cmdname] + self.args)
396 args = ' '.join([self.cmdname] + self.args)
397 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
397 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
398 raise
398 raise
399
399
400 def addaliases(ui, cmdtable):
400 def addaliases(ui, cmdtable):
401 # aliases are processed after extensions have been loaded, so they
401 # aliases are processed after extensions have been loaded, so they
402 # may use extension commands. Aliases can also use other alias definitions,
402 # may use extension commands. Aliases can also use other alias definitions,
403 # but only if they have been defined prior to the current definition.
403 # but only if they have been defined prior to the current definition.
404 for alias, definition in ui.configitems('alias'):
404 for alias, definition in ui.configitems('alias'):
405 aliasdef = cmdalias(alias, definition, cmdtable)
405 aliasdef = cmdalias(alias, definition, cmdtable)
406
406
407 try:
407 try:
408 olddef = cmdtable[aliasdef.cmd][0]
408 olddef = cmdtable[aliasdef.cmd][0]
409 if olddef.definition == aliasdef.definition:
409 if olddef.definition == aliasdef.definition:
410 continue
410 continue
411 except (KeyError, AttributeError):
411 except (KeyError, AttributeError):
412 # definition might not exist or it might not be a cmdalias
412 # definition might not exist or it might not be a cmdalias
413 pass
413 pass
414
414
415 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
415 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
416 if aliasdef.norepo:
416 if aliasdef.norepo:
417 commands.norepo += ' %s' % alias
417 commands.norepo += ' %s' % alias
418 if aliasdef.optionalrepo:
418 if aliasdef.optionalrepo:
419 commands.optionalrepo += ' %s' % alias
419 commands.optionalrepo += ' %s' % alias
420
420
421 def _parse(ui, args):
421 def _parse(ui, args):
422 options = {}
422 options = {}
423 cmdoptions = {}
423 cmdoptions = {}
424
424
425 try:
425 try:
426 args = fancyopts.fancyopts(args, commands.globalopts, options)
426 args = fancyopts.fancyopts(args, commands.globalopts, options)
427 except fancyopts.getopt.GetoptError, inst:
427 except fancyopts.getopt.GetoptError, inst:
428 raise error.CommandError(None, inst)
428 raise error.CommandError(None, inst)
429
429
430 if args:
430 if args:
431 cmd, args = args[0], args[1:]
431 cmd, args = args[0], args[1:]
432 aliases, entry = cmdutil.findcmd(cmd, commands.table,
432 aliases, entry = cmdutil.findcmd(cmd, commands.table,
433 ui.configbool("ui", "strict"))
433 ui.configbool("ui", "strict"))
434 cmd = aliases[0]
434 cmd = aliases[0]
435 args = aliasargs(entry[0], args)
435 args = aliasargs(entry[0], args)
436 defaults = ui.config("defaults", cmd)
436 defaults = ui.config("defaults", cmd)
437 if defaults:
437 if defaults:
438 args = map(util.expandpath, shlex.split(defaults)) + args
438 args = map(util.expandpath, shlex.split(defaults)) + args
439 c = list(entry[1])
439 c = list(entry[1])
440 else:
440 else:
441 cmd = None
441 cmd = None
442 c = []
442 c = []
443
443
444 # combine global options into local
444 # combine global options into local
445 for o in commands.globalopts:
445 for o in commands.globalopts:
446 c.append((o[0], o[1], options[o[1]], o[3]))
446 c.append((o[0], o[1], options[o[1]], o[3]))
447
447
448 try:
448 try:
449 args = fancyopts.fancyopts(args, c, cmdoptions, True)
449 args = fancyopts.fancyopts(args, c, cmdoptions, True)
450 except fancyopts.getopt.GetoptError, inst:
450 except fancyopts.getopt.GetoptError, inst:
451 raise error.CommandError(cmd, inst)
451 raise error.CommandError(cmd, inst)
452
452
453 # separate global options back out
453 # separate global options back out
454 for o in commands.globalopts:
454 for o in commands.globalopts:
455 n = o[1]
455 n = o[1]
456 options[n] = cmdoptions[n]
456 options[n] = cmdoptions[n]
457 del cmdoptions[n]
457 del cmdoptions[n]
458
458
459 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
459 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
460
460
461 def _parseconfig(ui, config):
461 def _parseconfig(ui, config):
462 """parse the --config options from the command line"""
462 """parse the --config options from the command line"""
463 configs = []
463 configs = []
464
464
465 for cfg in config:
465 for cfg in config:
466 try:
466 try:
467 name, value = cfg.split('=', 1)
467 name, value = cfg.split('=', 1)
468 section, name = name.split('.', 1)
468 section, name = name.split('.', 1)
469 if not section or not name:
469 if not section or not name:
470 raise IndexError
470 raise IndexError
471 ui.setconfig(section, name, value)
471 ui.setconfig(section, name, value)
472 configs.append((section, name, value))
472 configs.append((section, name, value))
473 except (IndexError, ValueError):
473 except (IndexError, ValueError):
474 raise util.Abort(_('malformed --config option: %r '
474 raise util.Abort(_('malformed --config option: %r '
475 '(use --config section.name=value)') % cfg)
475 '(use --config section.name=value)') % cfg)
476
476
477 return configs
477 return configs
478
478
479 def _earlygetopt(aliases, args):
479 def _earlygetopt(aliases, args):
480 """Return list of values for an option (or aliases).
480 """Return list of values for an option (or aliases).
481
481
482 The values are listed in the order they appear in args.
482 The values are listed in the order they appear in args.
483 The options and values are removed from args.
483 The options and values are removed from args.
484 """
484 """
485 try:
485 try:
486 argcount = args.index("--")
486 argcount = args.index("--")
487 except ValueError:
487 except ValueError:
488 argcount = len(args)
488 argcount = len(args)
489 shortopts = [opt for opt in aliases if len(opt) == 2]
489 shortopts = [opt for opt in aliases if len(opt) == 2]
490 values = []
490 values = []
491 pos = 0
491 pos = 0
492 while pos < argcount:
492 while pos < argcount:
493 if args[pos] in aliases:
493 if args[pos] in aliases:
494 if pos + 1 >= argcount:
494 if pos + 1 >= argcount:
495 # ignore and let getopt report an error if there is no value
495 # ignore and let getopt report an error if there is no value
496 break
496 break
497 del args[pos]
497 del args[pos]
498 values.append(args.pop(pos))
498 values.append(args.pop(pos))
499 argcount -= 2
499 argcount -= 2
500 elif args[pos][:2] in shortopts:
500 elif args[pos][:2] in shortopts:
501 # short option can have no following space, e.g. hg log -Rfoo
501 # short option can have no following space, e.g. hg log -Rfoo
502 values.append(args.pop(pos)[2:])
502 values.append(args.pop(pos)[2:])
503 argcount -= 1
503 argcount -= 1
504 else:
504 else:
505 pos += 1
505 pos += 1
506 return values
506 return values
507
507
508 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
508 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
509 # run pre-hook, and abort if it fails
509 # run pre-hook, and abort if it fails
510 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
510 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
511 pats=cmdpats, opts=cmdoptions)
511 pats=cmdpats, opts=cmdoptions)
512 if ret:
512 if ret:
513 return ret
513 return ret
514 ret = _runcommand(ui, options, cmd, d)
514 ret = _runcommand(ui, options, cmd, d)
515 # run post-hook, passing command result
515 # run post-hook, passing command result
516 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
516 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
517 result=ret, pats=cmdpats, opts=cmdoptions)
517 result=ret, pats=cmdpats, opts=cmdoptions)
518 return ret
518 return ret
519
519
520 def _getlocal(ui, rpath):
520 def _getlocal(ui, rpath):
521 """Return (path, local ui object) for the given target path.
521 """Return (path, local ui object) for the given target path.
522
522
523 Takes paths in [cwd]/.hg/hgrc into account."
523 Takes paths in [cwd]/.hg/hgrc into account."
524 """
524 """
525 try:
525 try:
526 wd = os.getcwd()
526 wd = os.getcwd()
527 except OSError, e:
527 except OSError, e:
528 raise util.Abort(_("error getting current working directory: %s") %
528 raise util.Abort(_("error getting current working directory: %s") %
529 e.strerror)
529 e.strerror)
530 path = cmdutil.findrepo(wd) or ""
530 path = cmdutil.findrepo(wd) or ""
531 if not path:
531 if not path:
532 lui = ui
532 lui = ui
533 else:
533 else:
534 lui = ui.copy()
534 lui = ui.copy()
535 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
535 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
536
536
537 if rpath and rpath[-1]:
537 if rpath and rpath[-1]:
538 path = lui.expandpath(rpath[-1])
538 path = lui.expandpath(rpath[-1])
539 lui = ui.copy()
539 lui = ui.copy()
540 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
540 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
541
541
542 return path, lui
542 return path, lui
543
543
544 def _checkshellalias(lui, ui, args):
544 def _checkshellalias(lui, ui, args):
545 options = {}
545 options = {}
546
546
547 try:
547 try:
548 args = fancyopts.fancyopts(args, commands.globalopts, options)
548 args = fancyopts.fancyopts(args, commands.globalopts, options)
549 except fancyopts.getopt.GetoptError:
549 except fancyopts.getopt.GetoptError:
550 return
550 return
551
551
552 if not args:
552 if not args:
553 return
553 return
554
554
555 norepo = commands.norepo
555 norepo = commands.norepo
556 optionalrepo = commands.optionalrepo
556 optionalrepo = commands.optionalrepo
557 def restorecommands():
557 def restorecommands():
558 commands.norepo = norepo
558 commands.norepo = norepo
559 commands.optionalrepo = optionalrepo
559 commands.optionalrepo = optionalrepo
560
560
561 cmdtable = commands.table.copy()
561 cmdtable = commands.table.copy()
562 addaliases(lui, cmdtable)
562 addaliases(lui, cmdtable)
563
563
564 cmd = args[0]
564 cmd = args[0]
565 try:
565 try:
566 aliases, entry = cmdutil.findcmd(cmd, cmdtable,
566 aliases, entry = cmdutil.findcmd(cmd, cmdtable,
567 lui.configbool("ui", "strict"))
567 lui.configbool("ui", "strict"))
568 except (error.AmbiguousCommand, error.UnknownCommand):
568 except (error.AmbiguousCommand, error.UnknownCommand):
569 restorecommands()
569 restorecommands()
570 return
570 return
571
571
572 cmd = aliases[0]
572 cmd = aliases[0]
573 fn = entry[0]
573 fn = entry[0]
574
574
575 if cmd and util.safehasattr(fn, 'shell'):
575 if cmd and util.safehasattr(fn, 'shell'):
576 d = lambda: fn(ui, *args[1:])
576 d = lambda: fn(ui, *args[1:])
577 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
577 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
578 [], {})
578 [], {})
579
579
580 restorecommands()
580 restorecommands()
581
581
582 _loaded = set()
582 _loaded = set()
583 def _dispatch(req):
583 def _dispatch(req):
584 args = req.args
584 args = req.args
585 ui = req.ui
585 ui = req.ui
586
586
587 # read --config before doing anything else
587 # read --config before doing anything else
588 # (e.g. to change trust settings for reading .hg/hgrc)
588 # (e.g. to change trust settings for reading .hg/hgrc)
589 cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
589 cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
590
590
591 # check for cwd
591 # check for cwd
592 cwd = _earlygetopt(['--cwd'], args)
592 cwd = _earlygetopt(['--cwd'], args)
593 if cwd:
593 if cwd:
594 os.chdir(cwd[-1])
594 os.chdir(cwd[-1])
595
595
596 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
596 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
597 path, lui = _getlocal(ui, rpath)
597 path, lui = _getlocal(ui, rpath)
598
598
599 # Now that we're operating in the right directory/repository with
599 # Now that we're operating in the right directory/repository with
600 # the right config settings, check for shell aliases
600 # the right config settings, check for shell aliases
601 shellaliasfn = _checkshellalias(lui, ui, args)
601 shellaliasfn = _checkshellalias(lui, ui, args)
602 if shellaliasfn:
602 if shellaliasfn:
603 return shellaliasfn()
603 return shellaliasfn()
604
604
605 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
605 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
606 # reposetup. Programs like TortoiseHg will call _dispatch several
606 # reposetup. Programs like TortoiseHg will call _dispatch several
607 # times so we keep track of configured extensions in _loaded.
607 # times so we keep track of configured extensions in _loaded.
608 extensions.loadall(lui)
608 extensions.loadall(lui)
609 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
609 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
610 # Propagate any changes to lui.__class__ by extensions
610 # Propagate any changes to lui.__class__ by extensions
611 ui.__class__ = lui.__class__
611 ui.__class__ = lui.__class__
612
612
613 # (uisetup and extsetup are handled in extensions.loadall)
613 # (uisetup and extsetup are handled in extensions.loadall)
614
614
615 for name, module in exts:
615 for name, module in exts:
616 cmdtable = getattr(module, 'cmdtable', {})
616 cmdtable = getattr(module, 'cmdtable', {})
617 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
617 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
618 if overrides:
618 if overrides:
619 ui.warn(_("extension '%s' overrides commands: %s\n")
619 ui.warn(_("extension '%s' overrides commands: %s\n")
620 % (name, " ".join(overrides)))
620 % (name, " ".join(overrides)))
621 commands.table.update(cmdtable)
621 commands.table.update(cmdtable)
622 _loaded.add(name)
622 _loaded.add(name)
623
623
624 # (reposetup is handled in hg.repository)
624 # (reposetup is handled in hg.repository)
625
625
626 addaliases(lui, commands.table)
626 addaliases(lui, commands.table)
627
627
628 # check for fallback encoding
628 # check for fallback encoding
629 fallback = lui.config('ui', 'fallbackencoding')
629 fallback = lui.config('ui', 'fallbackencoding')
630 if fallback:
630 if fallback:
631 encoding.fallbackencoding = fallback
631 encoding.fallbackencoding = fallback
632
632
633 fullargs = args
633 fullargs = args
634 cmd, func, args, options, cmdoptions = _parse(lui, args)
634 cmd, func, args, options, cmdoptions = _parse(lui, args)
635
635
636 if options["config"]:
636 if options["config"]:
637 raise util.Abort(_("option --config may not be abbreviated!"))
637 raise util.Abort(_("option --config may not be abbreviated!"))
638 if options["cwd"]:
638 if options["cwd"]:
639 raise util.Abort(_("option --cwd may not be abbreviated!"))
639 raise util.Abort(_("option --cwd may not be abbreviated!"))
640 if options["repository"]:
640 if options["repository"]:
641 raise util.Abort(_(
641 raise util.Abort(_(
642 "option -R has to be separated from other options (e.g. not -qR) "
642 "option -R has to be separated from other options (e.g. not -qR) "
643 "and --repository may only be abbreviated as --repo!"))
643 "and --repository may only be abbreviated as --repo!"))
644
644
645 if options["encoding"]:
645 if options["encoding"]:
646 encoding.encoding = options["encoding"]
646 encoding.encoding = options["encoding"]
647 if options["encodingmode"]:
647 if options["encodingmode"]:
648 encoding.encodingmode = options["encodingmode"]
648 encoding.encodingmode = options["encodingmode"]
649 if options["time"]:
649 if options["time"]:
650 def get_times():
650 def get_times():
651 t = os.times()
651 t = os.times()
652 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
652 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
653 t = (t[0], t[1], t[2], t[3], time.clock())
653 t = (t[0], t[1], t[2], t[3], time.clock())
654 return t
654 return t
655 s = get_times()
655 s = get_times()
656 def print_time():
656 def print_time():
657 t = get_times()
657 t = get_times()
658 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
658 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
659 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
659 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
660 atexit.register(print_time)
660 atexit.register(print_time)
661
661
662 uis = set([ui, lui])
662 uis = set([ui, lui])
663
663
664 if req.repo:
664 if req.repo:
665 uis.add(req.repo.ui)
665 uis.add(req.repo.ui)
666
666
667 # copy configs that were passed on the cmdline (--config) to the repo ui
667 # copy configs that were passed on the cmdline (--config) to the repo ui
668 for cfg in cfgs:
668 for cfg in cfgs:
669 req.repo.ui.setconfig(*cfg)
669 req.repo.ui.setconfig(*cfg)
670
670
671 if options['verbose'] or options['debug'] or options['quiet']:
671 if options['verbose'] or options['debug'] or options['quiet']:
672 for opt in ('verbose', 'debug', 'quiet'):
672 for opt in ('verbose', 'debug', 'quiet'):
673 val = str(bool(options[opt]))
673 val = str(bool(options[opt]))
674 for ui_ in uis:
674 for ui_ in uis:
675 ui_.setconfig('ui', opt, val)
675 ui_.setconfig('ui', opt, val)
676
676
677 if options['traceback']:
677 if options['traceback']:
678 for ui_ in uis:
678 for ui_ in uis:
679 ui_.setconfig('ui', 'traceback', 'on')
679 ui_.setconfig('ui', 'traceback', 'on')
680
680
681 if options['noninteractive']:
681 if options['noninteractive']:
682 for ui_ in uis:
682 for ui_ in uis:
683 ui_.setconfig('ui', 'interactive', 'off')
683 ui_.setconfig('ui', 'interactive', 'off')
684
684
685 if cmdoptions.get('insecure', False):
685 if cmdoptions.get('insecure', False):
686 for ui_ in uis:
686 for ui_ in uis:
687 ui_.setconfig('web', 'cacerts', '')
687 ui_.setconfig('web', 'cacerts', '')
688
688
689 if options['version']:
689 if options['version']:
690 return commands.version_(ui)
690 return commands.version_(ui)
691 if options['help']:
691 if options['help']:
692 return commands.help_(ui, cmd)
692 return commands.help_(ui, cmd)
693 elif not cmd:
693 elif not cmd:
694 return commands.help_(ui, 'shortlist')
694 return commands.help_(ui, 'shortlist')
695
695
696 repo = None
696 repo = None
697 cmdpats = args[:]
697 cmdpats = args[:]
698 if cmd not in commands.norepo.split():
698 if cmd not in commands.norepo.split():
699 # use the repo from the request only if we don't have -R
699 # use the repo from the request only if we don't have -R
700 if not rpath and not cwd:
700 if not rpath and not cwd:
701 repo = req.repo
701 repo = req.repo
702
702
703 if repo:
703 if repo:
704 # set the descriptors of the repo ui to those of ui
704 # set the descriptors of the repo ui to those of ui
705 repo.ui.fin = ui.fin
705 repo.ui.fin = ui.fin
706 repo.ui.fout = ui.fout
706 repo.ui.fout = ui.fout
707 repo.ui.ferr = ui.ferr
707 repo.ui.ferr = ui.ferr
708 else:
708 else:
709 try:
709 try:
710 repo = hg.repository(ui, path=path)
710 repo = hg.repository(ui, path=path)
711 if not repo.local():
711 if not repo.local():
712 raise util.Abort(_("repository '%s' is not local") % path)
712 raise util.Abort(_("repository '%s' is not local") % path)
713 if not options['hidden']:
713 if options['hidden']:
714 repo = repo.filtered('hidden')
715 else:
716 repo = repo.unfiltered()
714 repo = repo.unfiltered()
717 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
715 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
718 except error.RequirementError:
716 except error.RequirementError:
719 raise
717 raise
720 except error.RepoError:
718 except error.RepoError:
721 if cmd not in commands.optionalrepo.split():
719 if cmd not in commands.optionalrepo.split():
722 if (cmd in commands.inferrepo.split() and
720 if (cmd in commands.inferrepo.split() and
723 args and not path): # try to infer -R from command args
721 args and not path): # try to infer -R from command args
724 repos = map(cmdutil.findrepo, args)
722 repos = map(cmdutil.findrepo, args)
725 guess = repos[0]
723 guess = repos[0]
726 if guess and repos.count(guess) == len(repos):
724 if guess and repos.count(guess) == len(repos):
727 req.args = ['--repository', guess] + fullargs
725 req.args = ['--repository', guess] + fullargs
728 return _dispatch(req)
726 return _dispatch(req)
729 if not path:
727 if not path:
730 raise error.RepoError(_("no repository found in '%s'"
728 raise error.RepoError(_("no repository found in '%s'"
731 " (.hg not found)")
729 " (.hg not found)")
732 % os.getcwd())
730 % os.getcwd())
733 raise
731 raise
734 if repo:
732 if repo:
735 ui = repo.ui
733 ui = repo.ui
736 args.insert(0, repo)
734 args.insert(0, repo)
737 elif rpath:
735 elif rpath:
738 ui.warn(_("warning: --repository ignored\n"))
736 ui.warn(_("warning: --repository ignored\n"))
739
737
740 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
738 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
741 ui.log("command", msg + "\n")
739 ui.log("command", msg + "\n")
742 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
740 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
743 try:
741 try:
744 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
742 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
745 cmdpats, cmdoptions)
743 cmdpats, cmdoptions)
746 finally:
744 finally:
747 if repo and repo != req.repo:
745 if repo and repo != req.repo:
748 repo.close()
746 repo.close()
749
747
750 def lsprofile(ui, func, fp):
748 def lsprofile(ui, func, fp):
751 format = ui.config('profiling', 'format', default='text')
749 format = ui.config('profiling', 'format', default='text')
752 field = ui.config('profiling', 'sort', default='inlinetime')
750 field = ui.config('profiling', 'sort', default='inlinetime')
753 climit = ui.configint('profiling', 'nested', default=5)
751 climit = ui.configint('profiling', 'nested', default=5)
754
752
755 if format not in ['text', 'kcachegrind']:
753 if format not in ['text', 'kcachegrind']:
756 ui.warn(_("unrecognized profiling format '%s'"
754 ui.warn(_("unrecognized profiling format '%s'"
757 " - Ignored\n") % format)
755 " - Ignored\n") % format)
758 format = 'text'
756 format = 'text'
759
757
760 try:
758 try:
761 from mercurial import lsprof
759 from mercurial import lsprof
762 except ImportError:
760 except ImportError:
763 raise util.Abort(_(
761 raise util.Abort(_(
764 'lsprof not available - install from '
762 'lsprof not available - install from '
765 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
763 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
766 p = lsprof.Profiler()
764 p = lsprof.Profiler()
767 p.enable(subcalls=True)
765 p.enable(subcalls=True)
768 try:
766 try:
769 return func()
767 return func()
770 finally:
768 finally:
771 p.disable()
769 p.disable()
772
770
773 if format == 'kcachegrind':
771 if format == 'kcachegrind':
774 import lsprofcalltree
772 import lsprofcalltree
775 calltree = lsprofcalltree.KCacheGrind(p)
773 calltree = lsprofcalltree.KCacheGrind(p)
776 calltree.output(fp)
774 calltree.output(fp)
777 else:
775 else:
778 # format == 'text'
776 # format == 'text'
779 stats = lsprof.Stats(p.getstats())
777 stats = lsprof.Stats(p.getstats())
780 stats.sort(field)
778 stats.sort(field)
781 stats.pprint(limit=30, file=fp, climit=climit)
779 stats.pprint(limit=30, file=fp, climit=climit)
782
780
783 def statprofile(ui, func, fp):
781 def statprofile(ui, func, fp):
784 try:
782 try:
785 import statprof
783 import statprof
786 except ImportError:
784 except ImportError:
787 raise util.Abort(_(
785 raise util.Abort(_(
788 'statprof not available - install using "easy_install statprof"'))
786 'statprof not available - install using "easy_install statprof"'))
789
787
790 freq = ui.configint('profiling', 'freq', default=1000)
788 freq = ui.configint('profiling', 'freq', default=1000)
791 if freq > 0:
789 if freq > 0:
792 statprof.reset(freq)
790 statprof.reset(freq)
793 else:
791 else:
794 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
792 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
795
793
796 statprof.start()
794 statprof.start()
797 try:
795 try:
798 return func()
796 return func()
799 finally:
797 finally:
800 statprof.stop()
798 statprof.stop()
801 statprof.display(fp)
799 statprof.display(fp)
802
800
803 def _runcommand(ui, options, cmd, cmdfunc):
801 def _runcommand(ui, options, cmd, cmdfunc):
804 def checkargs():
802 def checkargs():
805 try:
803 try:
806 return cmdfunc()
804 return cmdfunc()
807 except error.SignatureError:
805 except error.SignatureError:
808 raise error.CommandError(cmd, _("invalid arguments"))
806 raise error.CommandError(cmd, _("invalid arguments"))
809
807
810 if options['profile']:
808 if options['profile']:
811 profiler = os.getenv('HGPROF')
809 profiler = os.getenv('HGPROF')
812 if profiler is None:
810 if profiler is None:
813 profiler = ui.config('profiling', 'type', default='ls')
811 profiler = ui.config('profiling', 'type', default='ls')
814 if profiler not in ('ls', 'stat'):
812 if profiler not in ('ls', 'stat'):
815 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
813 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
816 profiler = 'ls'
814 profiler = 'ls'
817
815
818 output = ui.config('profiling', 'output')
816 output = ui.config('profiling', 'output')
819
817
820 if output:
818 if output:
821 path = ui.expandpath(output)
819 path = ui.expandpath(output)
822 fp = open(path, 'wb')
820 fp = open(path, 'wb')
823 else:
821 else:
824 fp = sys.stderr
822 fp = sys.stderr
825
823
826 try:
824 try:
827 if profiler == 'ls':
825 if profiler == 'ls':
828 return lsprofile(ui, checkargs, fp)
826 return lsprofile(ui, checkargs, fp)
829 else:
827 else:
830 return statprofile(ui, checkargs, fp)
828 return statprofile(ui, checkargs, fp)
831 finally:
829 finally:
832 if output:
830 if output:
833 fp.close()
831 fp.close()
834 else:
832 else:
835 return checkargs()
833 return checkargs()
@@ -1,646 +1,646 b''
1 # hg.py - repository classes for mercurial
1 # hg.py - repository classes for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from i18n import _
9 from i18n import _
10 from lock import release
10 from lock import release
11 from node import hex, nullid
11 from node import hex, nullid
12 import localrepo, bundlerepo, httppeer, sshpeer, statichttprepo, bookmarks
12 import localrepo, bundlerepo, httppeer, sshpeer, statichttprepo, bookmarks
13 import lock, util, extensions, error, node, scmutil, phases, url
13 import lock, util, extensions, error, node, scmutil, phases, url
14 import cmdutil, discovery
14 import cmdutil, discovery
15 import merge as mergemod
15 import merge as mergemod
16 import verify as verifymod
16 import verify as verifymod
17 import errno, os, shutil
17 import errno, os, shutil
18
18
19 def _local(path):
19 def _local(path):
20 path = util.expandpath(util.urllocalpath(path))
20 path = util.expandpath(util.urllocalpath(path))
21 return (os.path.isfile(path) and bundlerepo or localrepo)
21 return (os.path.isfile(path) and bundlerepo or localrepo)
22
22
23 def addbranchrevs(lrepo, other, branches, revs):
23 def addbranchrevs(lrepo, other, branches, revs):
24 peer = other.peer() # a courtesy to callers using a localrepo for other
24 peer = other.peer() # a courtesy to callers using a localrepo for other
25 hashbranch, branches = branches
25 hashbranch, branches = branches
26 if not hashbranch and not branches:
26 if not hashbranch and not branches:
27 return revs or None, revs and revs[0] or None
27 return revs or None, revs and revs[0] or None
28 revs = revs and list(revs) or []
28 revs = revs and list(revs) or []
29 if not peer.capable('branchmap'):
29 if not peer.capable('branchmap'):
30 if branches:
30 if branches:
31 raise util.Abort(_("remote branch lookup not supported"))
31 raise util.Abort(_("remote branch lookup not supported"))
32 revs.append(hashbranch)
32 revs.append(hashbranch)
33 return revs, revs[0]
33 return revs, revs[0]
34 branchmap = peer.branchmap()
34 branchmap = peer.branchmap()
35
35
36 def primary(branch):
36 def primary(branch):
37 if branch == '.':
37 if branch == '.':
38 if not lrepo:
38 if not lrepo:
39 raise util.Abort(_("dirstate branch not accessible"))
39 raise util.Abort(_("dirstate branch not accessible"))
40 branch = lrepo.dirstate.branch()
40 branch = lrepo.dirstate.branch()
41 if branch in branchmap:
41 if branch in branchmap:
42 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
42 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
43 return True
43 return True
44 else:
44 else:
45 return False
45 return False
46
46
47 for branch in branches:
47 for branch in branches:
48 if not primary(branch):
48 if not primary(branch):
49 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
49 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
50 if hashbranch:
50 if hashbranch:
51 if not primary(hashbranch):
51 if not primary(hashbranch):
52 revs.append(hashbranch)
52 revs.append(hashbranch)
53 return revs, revs[0]
53 return revs, revs[0]
54
54
55 def parseurl(path, branches=None):
55 def parseurl(path, branches=None):
56 '''parse url#branch, returning (url, (branch, branches))'''
56 '''parse url#branch, returning (url, (branch, branches))'''
57
57
58 u = util.url(path)
58 u = util.url(path)
59 branch = None
59 branch = None
60 if u.fragment:
60 if u.fragment:
61 branch = u.fragment
61 branch = u.fragment
62 u.fragment = None
62 u.fragment = None
63 return str(u), (branch, branches or [])
63 return str(u), (branch, branches or [])
64
64
65 schemes = {
65 schemes = {
66 'bundle': bundlerepo,
66 'bundle': bundlerepo,
67 'file': _local,
67 'file': _local,
68 'http': httppeer,
68 'http': httppeer,
69 'https': httppeer,
69 'https': httppeer,
70 'ssh': sshpeer,
70 'ssh': sshpeer,
71 'static-http': statichttprepo,
71 'static-http': statichttprepo,
72 }
72 }
73
73
74 def _peerlookup(path):
74 def _peerlookup(path):
75 u = util.url(path)
75 u = util.url(path)
76 scheme = u.scheme or 'file'
76 scheme = u.scheme or 'file'
77 thing = schemes.get(scheme) or schemes['file']
77 thing = schemes.get(scheme) or schemes['file']
78 try:
78 try:
79 return thing(path)
79 return thing(path)
80 except TypeError:
80 except TypeError:
81 return thing
81 return thing
82
82
83 def islocal(repo):
83 def islocal(repo):
84 '''return true if repo or path is local'''
84 '''return true if repo or path is local'''
85 if isinstance(repo, str):
85 if isinstance(repo, str):
86 try:
86 try:
87 return _peerlookup(repo).islocal(repo)
87 return _peerlookup(repo).islocal(repo)
88 except AttributeError:
88 except AttributeError:
89 return False
89 return False
90 return repo.local()
90 return repo.local()
91
91
92 def openpath(ui, path):
92 def openpath(ui, path):
93 '''open path with open if local, url.open if remote'''
93 '''open path with open if local, url.open if remote'''
94 if islocal(path):
94 if islocal(path):
95 return util.posixfile(util.urllocalpath(path), 'rb')
95 return util.posixfile(util.urllocalpath(path), 'rb')
96 else:
96 else:
97 return url.open(ui, path)
97 return url.open(ui, path)
98
98
99 def _peerorrepo(ui, path, create=False):
99 def _peerorrepo(ui, path, create=False):
100 """return a repository object for the specified path"""
100 """return a repository object for the specified path"""
101 obj = _peerlookup(path).instance(ui, path, create)
101 obj = _peerlookup(path).instance(ui, path, create)
102 ui = getattr(obj, "ui", ui)
102 ui = getattr(obj, "ui", ui)
103 for name, module in extensions.extensions():
103 for name, module in extensions.extensions():
104 hook = getattr(module, 'reposetup', None)
104 hook = getattr(module, 'reposetup', None)
105 if hook:
105 if hook:
106 hook(ui, obj)
106 hook(ui, obj)
107 return obj
107 return obj
108
108
109 def repository(ui, path='', create=False):
109 def repository(ui, path='', create=False):
110 """return a repository object for the specified path"""
110 """return a repository object for the specified path"""
111 peer = _peerorrepo(ui, path, create)
111 peer = _peerorrepo(ui, path, create)
112 repo = peer.local()
112 repo = peer.local()
113 if not repo:
113 if not repo:
114 raise util.Abort(_("repository '%s' is not local") %
114 raise util.Abort(_("repository '%s' is not local") %
115 (path or peer.url()))
115 (path or peer.url()))
116 return repo
116 return repo.filtered('hidden')
117
117
118 def peer(uiorrepo, opts, path, create=False):
118 def peer(uiorrepo, opts, path, create=False):
119 '''return a repository peer for the specified path'''
119 '''return a repository peer for the specified path'''
120 rui = remoteui(uiorrepo, opts)
120 rui = remoteui(uiorrepo, opts)
121 return _peerorrepo(rui, path, create).peer()
121 return _peerorrepo(rui, path, create).peer()
122
122
123 def defaultdest(source):
123 def defaultdest(source):
124 '''return default destination of clone if none is given'''
124 '''return default destination of clone if none is given'''
125 return os.path.basename(os.path.normpath(util.url(source).path))
125 return os.path.basename(os.path.normpath(util.url(source).path))
126
126
127 def share(ui, source, dest=None, update=True):
127 def share(ui, source, dest=None, update=True):
128 '''create a shared repository'''
128 '''create a shared repository'''
129
129
130 if not islocal(source):
130 if not islocal(source):
131 raise util.Abort(_('can only share local repositories'))
131 raise util.Abort(_('can only share local repositories'))
132
132
133 if not dest:
133 if not dest:
134 dest = defaultdest(source)
134 dest = defaultdest(source)
135 else:
135 else:
136 dest = ui.expandpath(dest)
136 dest = ui.expandpath(dest)
137
137
138 if isinstance(source, str):
138 if isinstance(source, str):
139 origsource = ui.expandpath(source)
139 origsource = ui.expandpath(source)
140 source, branches = parseurl(origsource)
140 source, branches = parseurl(origsource)
141 srcrepo = repository(ui, source)
141 srcrepo = repository(ui, source)
142 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
142 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
143 else:
143 else:
144 srcrepo = source.local()
144 srcrepo = source.local()
145 origsource = source = srcrepo.url()
145 origsource = source = srcrepo.url()
146 checkout = None
146 checkout = None
147
147
148 sharedpath = srcrepo.sharedpath # if our source is already sharing
148 sharedpath = srcrepo.sharedpath # if our source is already sharing
149
149
150 root = os.path.realpath(dest)
150 root = os.path.realpath(dest)
151 roothg = os.path.join(root, '.hg')
151 roothg = os.path.join(root, '.hg')
152
152
153 if os.path.exists(roothg):
153 if os.path.exists(roothg):
154 raise util.Abort(_('destination already exists'))
154 raise util.Abort(_('destination already exists'))
155
155
156 if not os.path.isdir(root):
156 if not os.path.isdir(root):
157 os.mkdir(root)
157 os.mkdir(root)
158 util.makedir(roothg, notindexed=True)
158 util.makedir(roothg, notindexed=True)
159
159
160 requirements = ''
160 requirements = ''
161 try:
161 try:
162 requirements = srcrepo.opener.read('requires')
162 requirements = srcrepo.opener.read('requires')
163 except IOError, inst:
163 except IOError, inst:
164 if inst.errno != errno.ENOENT:
164 if inst.errno != errno.ENOENT:
165 raise
165 raise
166
166
167 requirements += 'shared\n'
167 requirements += 'shared\n'
168 util.writefile(os.path.join(roothg, 'requires'), requirements)
168 util.writefile(os.path.join(roothg, 'requires'), requirements)
169 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
169 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
170
170
171 r = repository(ui, root)
171 r = repository(ui, root)
172
172
173 default = srcrepo.ui.config('paths', 'default')
173 default = srcrepo.ui.config('paths', 'default')
174 if not default:
174 if not default:
175 # set default to source for being able to clone subrepos
175 # set default to source for being able to clone subrepos
176 default = os.path.abspath(util.urllocalpath(origsource))
176 default = os.path.abspath(util.urllocalpath(origsource))
177 fp = r.opener("hgrc", "w", text=True)
177 fp = r.opener("hgrc", "w", text=True)
178 fp.write("[paths]\n")
178 fp.write("[paths]\n")
179 fp.write("default = %s\n" % default)
179 fp.write("default = %s\n" % default)
180 fp.close()
180 fp.close()
181 r.ui.setconfig('paths', 'default', default)
181 r.ui.setconfig('paths', 'default', default)
182
182
183 if update:
183 if update:
184 r.ui.status(_("updating working directory\n"))
184 r.ui.status(_("updating working directory\n"))
185 if update is not True:
185 if update is not True:
186 checkout = update
186 checkout = update
187 for test in (checkout, 'default', 'tip'):
187 for test in (checkout, 'default', 'tip'):
188 if test is None:
188 if test is None:
189 continue
189 continue
190 try:
190 try:
191 uprev = r.lookup(test)
191 uprev = r.lookup(test)
192 break
192 break
193 except error.RepoLookupError:
193 except error.RepoLookupError:
194 continue
194 continue
195 _update(r, uprev)
195 _update(r, uprev)
196
196
197 def copystore(ui, srcrepo, destpath):
197 def copystore(ui, srcrepo, destpath):
198 '''copy files from store of srcrepo in destpath
198 '''copy files from store of srcrepo in destpath
199
199
200 returns destlock
200 returns destlock
201 '''
201 '''
202 destlock = None
202 destlock = None
203 try:
203 try:
204 hardlink = None
204 hardlink = None
205 num = 0
205 num = 0
206 srcpublishing = srcrepo.ui.configbool('phases', 'publish', True)
206 srcpublishing = srcrepo.ui.configbool('phases', 'publish', True)
207 for f in srcrepo.store.copylist():
207 for f in srcrepo.store.copylist():
208 if srcpublishing and f.endswith('phaseroots'):
208 if srcpublishing and f.endswith('phaseroots'):
209 continue
209 continue
210 src = os.path.join(srcrepo.sharedpath, f)
210 src = os.path.join(srcrepo.sharedpath, f)
211 dst = os.path.join(destpath, f)
211 dst = os.path.join(destpath, f)
212 dstbase = os.path.dirname(dst)
212 dstbase = os.path.dirname(dst)
213 if dstbase and not os.path.exists(dstbase):
213 if dstbase and not os.path.exists(dstbase):
214 os.mkdir(dstbase)
214 os.mkdir(dstbase)
215 if os.path.exists(src):
215 if os.path.exists(src):
216 if dst.endswith('data'):
216 if dst.endswith('data'):
217 # lock to avoid premature writing to the target
217 # lock to avoid premature writing to the target
218 destlock = lock.lock(os.path.join(dstbase, "lock"))
218 destlock = lock.lock(os.path.join(dstbase, "lock"))
219 hardlink, n = util.copyfiles(src, dst, hardlink)
219 hardlink, n = util.copyfiles(src, dst, hardlink)
220 num += n
220 num += n
221 if hardlink:
221 if hardlink:
222 ui.debug("linked %d files\n" % num)
222 ui.debug("linked %d files\n" % num)
223 else:
223 else:
224 ui.debug("copied %d files\n" % num)
224 ui.debug("copied %d files\n" % num)
225 return destlock
225 return destlock
226 except: # re-raises
226 except: # re-raises
227 release(destlock)
227 release(destlock)
228 raise
228 raise
229
229
230 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
230 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
231 update=True, stream=False, branch=None):
231 update=True, stream=False, branch=None):
232 """Make a copy of an existing repository.
232 """Make a copy of an existing repository.
233
233
234 Create a copy of an existing repository in a new directory. The
234 Create a copy of an existing repository in a new directory. The
235 source and destination are URLs, as passed to the repository
235 source and destination are URLs, as passed to the repository
236 function. Returns a pair of repository peers, the source and
236 function. Returns a pair of repository peers, the source and
237 newly created destination.
237 newly created destination.
238
238
239 The location of the source is added to the new repository's
239 The location of the source is added to the new repository's
240 .hg/hgrc file, as the default to be used for future pulls and
240 .hg/hgrc file, as the default to be used for future pulls and
241 pushes.
241 pushes.
242
242
243 If an exception is raised, the partly cloned/updated destination
243 If an exception is raised, the partly cloned/updated destination
244 repository will be deleted.
244 repository will be deleted.
245
245
246 Arguments:
246 Arguments:
247
247
248 source: repository object or URL
248 source: repository object or URL
249
249
250 dest: URL of destination repository to create (defaults to base
250 dest: URL of destination repository to create (defaults to base
251 name of source repository)
251 name of source repository)
252
252
253 pull: always pull from source repository, even in local case
253 pull: always pull from source repository, even in local case
254
254
255 stream: stream raw data uncompressed from repository (fast over
255 stream: stream raw data uncompressed from repository (fast over
256 LAN, slow over WAN)
256 LAN, slow over WAN)
257
257
258 rev: revision to clone up to (implies pull=True)
258 rev: revision to clone up to (implies pull=True)
259
259
260 update: update working directory after clone completes, if
260 update: update working directory after clone completes, if
261 destination is local repository (True means update to default rev,
261 destination is local repository (True means update to default rev,
262 anything else is treated as a revision)
262 anything else is treated as a revision)
263
263
264 branch: branches to clone
264 branch: branches to clone
265 """
265 """
266
266
267 if isinstance(source, str):
267 if isinstance(source, str):
268 origsource = ui.expandpath(source)
268 origsource = ui.expandpath(source)
269 source, branch = parseurl(origsource, branch)
269 source, branch = parseurl(origsource, branch)
270 srcpeer = peer(ui, peeropts, source)
270 srcpeer = peer(ui, peeropts, source)
271 else:
271 else:
272 srcpeer = source.peer() # in case we were called with a localrepo
272 srcpeer = source.peer() # in case we were called with a localrepo
273 branch = (None, branch or [])
273 branch = (None, branch or [])
274 origsource = source = srcpeer.url()
274 origsource = source = srcpeer.url()
275 rev, checkout = addbranchrevs(srcpeer, srcpeer, branch, rev)
275 rev, checkout = addbranchrevs(srcpeer, srcpeer, branch, rev)
276
276
277 if dest is None:
277 if dest is None:
278 dest = defaultdest(source)
278 dest = defaultdest(source)
279 ui.status(_("destination directory: %s\n") % dest)
279 ui.status(_("destination directory: %s\n") % dest)
280 else:
280 else:
281 dest = ui.expandpath(dest)
281 dest = ui.expandpath(dest)
282
282
283 dest = util.urllocalpath(dest)
283 dest = util.urllocalpath(dest)
284 source = util.urllocalpath(source)
284 source = util.urllocalpath(source)
285
285
286 if not dest:
286 if not dest:
287 raise util.Abort(_("empty destination path is not valid"))
287 raise util.Abort(_("empty destination path is not valid"))
288 if os.path.exists(dest):
288 if os.path.exists(dest):
289 if not os.path.isdir(dest):
289 if not os.path.isdir(dest):
290 raise util.Abort(_("destination '%s' already exists") % dest)
290 raise util.Abort(_("destination '%s' already exists") % dest)
291 elif os.listdir(dest):
291 elif os.listdir(dest):
292 raise util.Abort(_("destination '%s' is not empty") % dest)
292 raise util.Abort(_("destination '%s' is not empty") % dest)
293
293
294 class DirCleanup(object):
294 class DirCleanup(object):
295 def __init__(self, dir_):
295 def __init__(self, dir_):
296 self.rmtree = shutil.rmtree
296 self.rmtree = shutil.rmtree
297 self.dir_ = dir_
297 self.dir_ = dir_
298 def close(self):
298 def close(self):
299 self.dir_ = None
299 self.dir_ = None
300 def cleanup(self):
300 def cleanup(self):
301 if self.dir_:
301 if self.dir_:
302 self.rmtree(self.dir_, True)
302 self.rmtree(self.dir_, True)
303
303
304 srclock = destlock = dircleanup = None
304 srclock = destlock = dircleanup = None
305 srcrepo = srcpeer.local()
305 srcrepo = srcpeer.local()
306 try:
306 try:
307 abspath = origsource
307 abspath = origsource
308 if islocal(origsource):
308 if islocal(origsource):
309 abspath = os.path.abspath(util.urllocalpath(origsource))
309 abspath = os.path.abspath(util.urllocalpath(origsource))
310
310
311 if islocal(dest):
311 if islocal(dest):
312 dircleanup = DirCleanup(dest)
312 dircleanup = DirCleanup(dest)
313
313
314 copy = False
314 copy = False
315 if (srcrepo and srcrepo.cancopy() and islocal(dest)
315 if (srcrepo and srcrepo.cancopy() and islocal(dest)
316 and not phases.hassecret(srcrepo)):
316 and not phases.hassecret(srcrepo)):
317 copy = not pull and not rev
317 copy = not pull and not rev
318
318
319 if copy:
319 if copy:
320 try:
320 try:
321 # we use a lock here because if we race with commit, we
321 # we use a lock here because if we race with commit, we
322 # can end up with extra data in the cloned revlogs that's
322 # can end up with extra data in the cloned revlogs that's
323 # not pointed to by changesets, thus causing verify to
323 # not pointed to by changesets, thus causing verify to
324 # fail
324 # fail
325 srclock = srcrepo.lock(wait=False)
325 srclock = srcrepo.lock(wait=False)
326 except error.LockError:
326 except error.LockError:
327 copy = False
327 copy = False
328
328
329 if copy:
329 if copy:
330 srcrepo.hook('preoutgoing', throw=True, source='clone')
330 srcrepo.hook('preoutgoing', throw=True, source='clone')
331 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
331 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
332 if not os.path.exists(dest):
332 if not os.path.exists(dest):
333 os.mkdir(dest)
333 os.mkdir(dest)
334 else:
334 else:
335 # only clean up directories we create ourselves
335 # only clean up directories we create ourselves
336 dircleanup.dir_ = hgdir
336 dircleanup.dir_ = hgdir
337 try:
337 try:
338 destpath = hgdir
338 destpath = hgdir
339 util.makedir(destpath, notindexed=True)
339 util.makedir(destpath, notindexed=True)
340 except OSError, inst:
340 except OSError, inst:
341 if inst.errno == errno.EEXIST:
341 if inst.errno == errno.EEXIST:
342 dircleanup.close()
342 dircleanup.close()
343 raise util.Abort(_("destination '%s' already exists")
343 raise util.Abort(_("destination '%s' already exists")
344 % dest)
344 % dest)
345 raise
345 raise
346
346
347 destlock = copystore(ui, srcrepo, destpath)
347 destlock = copystore(ui, srcrepo, destpath)
348
348
349 # Recomputing branch cache might be slow on big repos,
349 # Recomputing branch cache might be slow on big repos,
350 # so just copy it
350 # so just copy it
351 dstcachedir = os.path.join(destpath, 'cache')
351 dstcachedir = os.path.join(destpath, 'cache')
352 srcbranchcache = srcrepo.sjoin('cache/branchheads')
352 srcbranchcache = srcrepo.sjoin('cache/branchheads')
353 dstbranchcache = os.path.join(dstcachedir, 'branchheads')
353 dstbranchcache = os.path.join(dstcachedir, 'branchheads')
354 if os.path.exists(srcbranchcache):
354 if os.path.exists(srcbranchcache):
355 if not os.path.exists(dstcachedir):
355 if not os.path.exists(dstcachedir):
356 os.mkdir(dstcachedir)
356 os.mkdir(dstcachedir)
357 util.copyfile(srcbranchcache, dstbranchcache)
357 util.copyfile(srcbranchcache, dstbranchcache)
358
358
359 # we need to re-init the repo after manually copying the data
359 # we need to re-init the repo after manually copying the data
360 # into it
360 # into it
361 destpeer = peer(srcrepo, peeropts, dest)
361 destpeer = peer(srcrepo, peeropts, dest)
362 srcrepo.hook('outgoing', source='clone',
362 srcrepo.hook('outgoing', source='clone',
363 node=node.hex(node.nullid))
363 node=node.hex(node.nullid))
364 else:
364 else:
365 try:
365 try:
366 destpeer = peer(srcrepo or ui, peeropts, dest, create=True)
366 destpeer = peer(srcrepo or ui, peeropts, dest, create=True)
367 # only pass ui when no srcrepo
367 # only pass ui when no srcrepo
368 except OSError, inst:
368 except OSError, inst:
369 if inst.errno == errno.EEXIST:
369 if inst.errno == errno.EEXIST:
370 dircleanup.close()
370 dircleanup.close()
371 raise util.Abort(_("destination '%s' already exists")
371 raise util.Abort(_("destination '%s' already exists")
372 % dest)
372 % dest)
373 raise
373 raise
374
374
375 revs = None
375 revs = None
376 if rev:
376 if rev:
377 if not srcpeer.capable('lookup'):
377 if not srcpeer.capable('lookup'):
378 raise util.Abort(_("src repository does not support "
378 raise util.Abort(_("src repository does not support "
379 "revision lookup and so doesn't "
379 "revision lookup and so doesn't "
380 "support clone by revision"))
380 "support clone by revision"))
381 revs = [srcpeer.lookup(r) for r in rev]
381 revs = [srcpeer.lookup(r) for r in rev]
382 checkout = revs[0]
382 checkout = revs[0]
383 if destpeer.local():
383 if destpeer.local():
384 destpeer.local().clone(srcpeer, heads=revs, stream=stream)
384 destpeer.local().clone(srcpeer, heads=revs, stream=stream)
385 elif srcrepo:
385 elif srcrepo:
386 srcrepo.push(destpeer, revs=revs)
386 srcrepo.push(destpeer, revs=revs)
387 else:
387 else:
388 raise util.Abort(_("clone from remote to remote not supported"))
388 raise util.Abort(_("clone from remote to remote not supported"))
389
389
390 if dircleanup:
390 if dircleanup:
391 dircleanup.close()
391 dircleanup.close()
392
392
393 # clone all bookmarks except divergent ones
393 # clone all bookmarks except divergent ones
394 destrepo = destpeer.local()
394 destrepo = destpeer.local()
395 if destrepo and srcpeer.capable("pushkey"):
395 if destrepo and srcpeer.capable("pushkey"):
396 rb = srcpeer.listkeys('bookmarks')
396 rb = srcpeer.listkeys('bookmarks')
397 marks = destrepo._bookmarks
397 marks = destrepo._bookmarks
398 for k, n in rb.iteritems():
398 for k, n in rb.iteritems():
399 try:
399 try:
400 m = destrepo.lookup(n)
400 m = destrepo.lookup(n)
401 marks[k] = m
401 marks[k] = m
402 except error.RepoLookupError:
402 except error.RepoLookupError:
403 pass
403 pass
404 if rb:
404 if rb:
405 marks.write()
405 marks.write()
406 elif srcrepo and destpeer.capable("pushkey"):
406 elif srcrepo and destpeer.capable("pushkey"):
407 for k, n in srcrepo._bookmarks.iteritems():
407 for k, n in srcrepo._bookmarks.iteritems():
408 destpeer.pushkey('bookmarks', k, '', hex(n))
408 destpeer.pushkey('bookmarks', k, '', hex(n))
409
409
410 if destrepo:
410 if destrepo:
411 fp = destrepo.opener("hgrc", "w", text=True)
411 fp = destrepo.opener("hgrc", "w", text=True)
412 fp.write("[paths]\n")
412 fp.write("[paths]\n")
413 u = util.url(abspath)
413 u = util.url(abspath)
414 u.passwd = None
414 u.passwd = None
415 defaulturl = str(u)
415 defaulturl = str(u)
416 fp.write("default = %s\n" % defaulturl)
416 fp.write("default = %s\n" % defaulturl)
417 fp.close()
417 fp.close()
418
418
419 destrepo.ui.setconfig('paths', 'default', defaulturl)
419 destrepo.ui.setconfig('paths', 'default', defaulturl)
420
420
421 if update:
421 if update:
422 if update is not True:
422 if update is not True:
423 checkout = srcpeer.lookup(update)
423 checkout = srcpeer.lookup(update)
424 uprev = None
424 uprev = None
425 status = None
425 status = None
426 if checkout is not None:
426 if checkout is not None:
427 try:
427 try:
428 uprev = destrepo.lookup(checkout)
428 uprev = destrepo.lookup(checkout)
429 except error.RepoLookupError:
429 except error.RepoLookupError:
430 pass
430 pass
431 if uprev is None:
431 if uprev is None:
432 try:
432 try:
433 uprev = destrepo._bookmarks['@']
433 uprev = destrepo._bookmarks['@']
434 update = '@'
434 update = '@'
435 bn = destrepo[uprev].branch()
435 bn = destrepo[uprev].branch()
436 if bn == 'default':
436 if bn == 'default':
437 status = _("updating to bookmark @\n")
437 status = _("updating to bookmark @\n")
438 else:
438 else:
439 status = _("updating to bookmark @ on branch %s\n"
439 status = _("updating to bookmark @ on branch %s\n"
440 % bn)
440 % bn)
441 except KeyError:
441 except KeyError:
442 try:
442 try:
443 uprev = destrepo.branchtip('default')
443 uprev = destrepo.branchtip('default')
444 except error.RepoLookupError:
444 except error.RepoLookupError:
445 uprev = destrepo.lookup('tip')
445 uprev = destrepo.lookup('tip')
446 if not status:
446 if not status:
447 bn = destrepo[uprev].branch()
447 bn = destrepo[uprev].branch()
448 status = _("updating to branch %s\n") % bn
448 status = _("updating to branch %s\n") % bn
449 destrepo.ui.status(status)
449 destrepo.ui.status(status)
450 _update(destrepo, uprev)
450 _update(destrepo, uprev)
451 if update in destrepo._bookmarks:
451 if update in destrepo._bookmarks:
452 bookmarks.setcurrent(destrepo, update)
452 bookmarks.setcurrent(destrepo, update)
453
453
454 return srcpeer, destpeer
454 return srcpeer, destpeer
455 finally:
455 finally:
456 release(srclock, destlock)
456 release(srclock, destlock)
457 if dircleanup is not None:
457 if dircleanup is not None:
458 dircleanup.cleanup()
458 dircleanup.cleanup()
459 if srcpeer is not None:
459 if srcpeer is not None:
460 srcpeer.close()
460 srcpeer.close()
461
461
462 def _showstats(repo, stats):
462 def _showstats(repo, stats):
463 repo.ui.status(_("%d files updated, %d files merged, "
463 repo.ui.status(_("%d files updated, %d files merged, "
464 "%d files removed, %d files unresolved\n") % stats)
464 "%d files removed, %d files unresolved\n") % stats)
465
465
466 def updaterepo(repo, node, overwrite):
466 def updaterepo(repo, node, overwrite):
467 """Update the working directory to node.
467 """Update the working directory to node.
468
468
469 When overwrite is set, changes are clobbered, merged else
469 When overwrite is set, changes are clobbered, merged else
470
470
471 returns stats (see pydoc mercurial.merge.applyupdates)"""
471 returns stats (see pydoc mercurial.merge.applyupdates)"""
472 return mergemod.update(repo, node, False, overwrite, None)
472 return mergemod.update(repo, node, False, overwrite, None)
473
473
474 def update(repo, node):
474 def update(repo, node):
475 """update the working directory to node, merging linear changes"""
475 """update the working directory to node, merging linear changes"""
476 stats = updaterepo(repo, node, False)
476 stats = updaterepo(repo, node, False)
477 _showstats(repo, stats)
477 _showstats(repo, stats)
478 if stats[3]:
478 if stats[3]:
479 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
479 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
480 return stats[3] > 0
480 return stats[3] > 0
481
481
482 # naming conflict in clone()
482 # naming conflict in clone()
483 _update = update
483 _update = update
484
484
485 def clean(repo, node, show_stats=True):
485 def clean(repo, node, show_stats=True):
486 """forcibly switch the working directory to node, clobbering changes"""
486 """forcibly switch the working directory to node, clobbering changes"""
487 stats = updaterepo(repo, node, True)
487 stats = updaterepo(repo, node, True)
488 if show_stats:
488 if show_stats:
489 _showstats(repo, stats)
489 _showstats(repo, stats)
490 return stats[3] > 0
490 return stats[3] > 0
491
491
492 def merge(repo, node, force=None, remind=True):
492 def merge(repo, node, force=None, remind=True):
493 """Branch merge with node, resolving changes. Return true if any
493 """Branch merge with node, resolving changes. Return true if any
494 unresolved conflicts."""
494 unresolved conflicts."""
495 stats = mergemod.update(repo, node, True, force, False)
495 stats = mergemod.update(repo, node, True, force, False)
496 _showstats(repo, stats)
496 _showstats(repo, stats)
497 if stats[3]:
497 if stats[3]:
498 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
498 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
499 "or 'hg update -C .' to abandon\n"))
499 "or 'hg update -C .' to abandon\n"))
500 elif remind:
500 elif remind:
501 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
501 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
502 return stats[3] > 0
502 return stats[3] > 0
503
503
504 def _incoming(displaychlist, subreporecurse, ui, repo, source,
504 def _incoming(displaychlist, subreporecurse, ui, repo, source,
505 opts, buffered=False):
505 opts, buffered=False):
506 """
506 """
507 Helper for incoming / gincoming.
507 Helper for incoming / gincoming.
508 displaychlist gets called with
508 displaychlist gets called with
509 (remoterepo, incomingchangesetlist, displayer) parameters,
509 (remoterepo, incomingchangesetlist, displayer) parameters,
510 and is supposed to contain only code that can't be unified.
510 and is supposed to contain only code that can't be unified.
511 """
511 """
512 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
512 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
513 other = peer(repo, opts, source)
513 other = peer(repo, opts, source)
514 ui.status(_('comparing with %s\n') % util.hidepassword(source))
514 ui.status(_('comparing with %s\n') % util.hidepassword(source))
515 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
515 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
516
516
517 if revs:
517 if revs:
518 revs = [other.lookup(rev) for rev in revs]
518 revs = [other.lookup(rev) for rev in revs]
519 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
519 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
520 revs, opts["bundle"], opts["force"])
520 revs, opts["bundle"], opts["force"])
521 try:
521 try:
522 if not chlist:
522 if not chlist:
523 ui.status(_("no changes found\n"))
523 ui.status(_("no changes found\n"))
524 return subreporecurse()
524 return subreporecurse()
525
525
526 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
526 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
527
527
528 # XXX once graphlog extension makes it into core,
528 # XXX once graphlog extension makes it into core,
529 # should be replaced by a if graph/else
529 # should be replaced by a if graph/else
530 displaychlist(other, chlist, displayer)
530 displaychlist(other, chlist, displayer)
531
531
532 displayer.close()
532 displayer.close()
533 finally:
533 finally:
534 cleanupfn()
534 cleanupfn()
535 subreporecurse()
535 subreporecurse()
536 return 0 # exit code is zero since we found incoming changes
536 return 0 # exit code is zero since we found incoming changes
537
537
538 def incoming(ui, repo, source, opts):
538 def incoming(ui, repo, source, opts):
539 def subreporecurse():
539 def subreporecurse():
540 ret = 1
540 ret = 1
541 if opts.get('subrepos'):
541 if opts.get('subrepos'):
542 ctx = repo[None]
542 ctx = repo[None]
543 for subpath in sorted(ctx.substate):
543 for subpath in sorted(ctx.substate):
544 sub = ctx.sub(subpath)
544 sub = ctx.sub(subpath)
545 ret = min(ret, sub.incoming(ui, source, opts))
545 ret = min(ret, sub.incoming(ui, source, opts))
546 return ret
546 return ret
547
547
548 def display(other, chlist, displayer):
548 def display(other, chlist, displayer):
549 limit = cmdutil.loglimit(opts)
549 limit = cmdutil.loglimit(opts)
550 if opts.get('newest_first'):
550 if opts.get('newest_first'):
551 chlist.reverse()
551 chlist.reverse()
552 count = 0
552 count = 0
553 for n in chlist:
553 for n in chlist:
554 if limit is not None and count >= limit:
554 if limit is not None and count >= limit:
555 break
555 break
556 parents = [p for p in other.changelog.parents(n) if p != nullid]
556 parents = [p for p in other.changelog.parents(n) if p != nullid]
557 if opts.get('no_merges') and len(parents) == 2:
557 if opts.get('no_merges') and len(parents) == 2:
558 continue
558 continue
559 count += 1
559 count += 1
560 displayer.show(other[n])
560 displayer.show(other[n])
561 return _incoming(display, subreporecurse, ui, repo, source, opts)
561 return _incoming(display, subreporecurse, ui, repo, source, opts)
562
562
563 def _outgoing(ui, repo, dest, opts):
563 def _outgoing(ui, repo, dest, opts):
564 dest = ui.expandpath(dest or 'default-push', dest or 'default')
564 dest = ui.expandpath(dest or 'default-push', dest or 'default')
565 dest, branches = parseurl(dest, opts.get('branch'))
565 dest, branches = parseurl(dest, opts.get('branch'))
566 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
566 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
567 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
567 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
568 if revs:
568 if revs:
569 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
569 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
570
570
571 other = peer(repo, opts, dest)
571 other = peer(repo, opts, dest)
572 outgoing = discovery.findcommonoutgoing(repo, other, revs,
572 outgoing = discovery.findcommonoutgoing(repo, other, revs,
573 force=opts.get('force'))
573 force=opts.get('force'))
574 o = outgoing.missing
574 o = outgoing.missing
575 if not o:
575 if not o:
576 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
576 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
577 return None
577 return None
578 return o
578 return o
579
579
580 def outgoing(ui, repo, dest, opts):
580 def outgoing(ui, repo, dest, opts):
581 def recurse():
581 def recurse():
582 ret = 1
582 ret = 1
583 if opts.get('subrepos'):
583 if opts.get('subrepos'):
584 ctx = repo[None]
584 ctx = repo[None]
585 for subpath in sorted(ctx.substate):
585 for subpath in sorted(ctx.substate):
586 sub = ctx.sub(subpath)
586 sub = ctx.sub(subpath)
587 ret = min(ret, sub.outgoing(ui, dest, opts))
587 ret = min(ret, sub.outgoing(ui, dest, opts))
588 return ret
588 return ret
589
589
590 limit = cmdutil.loglimit(opts)
590 limit = cmdutil.loglimit(opts)
591 o = _outgoing(ui, repo, dest, opts)
591 o = _outgoing(ui, repo, dest, opts)
592 if o is None:
592 if o is None:
593 return recurse()
593 return recurse()
594
594
595 if opts.get('newest_first'):
595 if opts.get('newest_first'):
596 o.reverse()
596 o.reverse()
597 displayer = cmdutil.show_changeset(ui, repo, opts)
597 displayer = cmdutil.show_changeset(ui, repo, opts)
598 count = 0
598 count = 0
599 for n in o:
599 for n in o:
600 if limit is not None and count >= limit:
600 if limit is not None and count >= limit:
601 break
601 break
602 parents = [p for p in repo.changelog.parents(n) if p != nullid]
602 parents = [p for p in repo.changelog.parents(n) if p != nullid]
603 if opts.get('no_merges') and len(parents) == 2:
603 if opts.get('no_merges') and len(parents) == 2:
604 continue
604 continue
605 count += 1
605 count += 1
606 displayer.show(repo[n])
606 displayer.show(repo[n])
607 displayer.close()
607 displayer.close()
608 recurse()
608 recurse()
609 return 0 # exit code is zero since we found outgoing changes
609 return 0 # exit code is zero since we found outgoing changes
610
610
611 def revert(repo, node, choose):
611 def revert(repo, node, choose):
612 """revert changes to revision in node without updating dirstate"""
612 """revert changes to revision in node without updating dirstate"""
613 return mergemod.update(repo, node, False, True, choose)[3] > 0
613 return mergemod.update(repo, node, False, True, choose)[3] > 0
614
614
615 def verify(repo):
615 def verify(repo):
616 """verify the consistency of a repository"""
616 """verify the consistency of a repository"""
617 return verifymod.verify(repo)
617 return verifymod.verify(repo)
618
618
619 def remoteui(src, opts):
619 def remoteui(src, opts):
620 'build a remote ui from ui or repo and opts'
620 'build a remote ui from ui or repo and opts'
621 if util.safehasattr(src, 'baseui'): # looks like a repository
621 if util.safehasattr(src, 'baseui'): # looks like a repository
622 dst = src.baseui.copy() # drop repo-specific config
622 dst = src.baseui.copy() # drop repo-specific config
623 src = src.ui # copy target options from repo
623 src = src.ui # copy target options from repo
624 else: # assume it's a global ui object
624 else: # assume it's a global ui object
625 dst = src.copy() # keep all global options
625 dst = src.copy() # keep all global options
626
626
627 # copy ssh-specific options
627 # copy ssh-specific options
628 for o in 'ssh', 'remotecmd':
628 for o in 'ssh', 'remotecmd':
629 v = opts.get(o) or src.config('ui', o)
629 v = opts.get(o) or src.config('ui', o)
630 if v:
630 if v:
631 dst.setconfig("ui", o, v)
631 dst.setconfig("ui", o, v)
632
632
633 # copy bundle-specific options
633 # copy bundle-specific options
634 r = src.config('bundle', 'mainreporoot')
634 r = src.config('bundle', 'mainreporoot')
635 if r:
635 if r:
636 dst.setconfig('bundle', 'mainreporoot', r)
636 dst.setconfig('bundle', 'mainreporoot', r)
637
637
638 # copy selected local settings to the remote ui
638 # copy selected local settings to the remote ui
639 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
639 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
640 for key, val in src.configitems(sect):
640 for key, val in src.configitems(sect):
641 dst.setconfig(sect, key, val)
641 dst.setconfig(sect, key, val)
642 v = src.config('web', 'cacerts')
642 v = src.config('web', 'cacerts')
643 if v:
643 if v:
644 dst.setconfig('web', 'cacerts', util.expandpath(v))
644 dst.setconfig('web', 'cacerts', util.expandpath(v))
645
645
646 return dst
646 return dst
General Comments 0
You need to be logged in to leave comments. Login now