##// END OF EJS Templates
dispatch: show empty filename in OSError aborts...
Mads Kiilerich -
r18227:720308f7 default
parent child Browse files
Show More
@@ -1,831 +1,831 b''
1 1 # dispatch.py - command dispatching for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import _
9 9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
10 10 import util, commands, hg, fancyopts, extensions, hook, error
11 11 import cmdutil, encoding
12 12 import ui as uimod
13 13
14 14 class request(object):
15 15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
16 16 ferr=None):
17 17 self.args = args
18 18 self.ui = ui
19 19 self.repo = repo
20 20
21 21 # input/output/error streams
22 22 self.fin = fin
23 23 self.fout = fout
24 24 self.ferr = ferr
25 25
26 26 def run():
27 27 "run the command in sys.argv"
28 28 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
29 29
30 30 def dispatch(req):
31 31 "run the command specified in req.args"
32 32 if req.ferr:
33 33 ferr = req.ferr
34 34 elif req.ui:
35 35 ferr = req.ui.ferr
36 36 else:
37 37 ferr = sys.stderr
38 38
39 39 try:
40 40 if not req.ui:
41 41 req.ui = uimod.ui()
42 42 if '--traceback' in req.args:
43 43 req.ui.setconfig('ui', 'traceback', 'on')
44 44
45 45 # set ui streams from the request
46 46 if req.fin:
47 47 req.ui.fin = req.fin
48 48 if req.fout:
49 49 req.ui.fout = req.fout
50 50 if req.ferr:
51 51 req.ui.ferr = req.ferr
52 52 except util.Abort, inst:
53 53 ferr.write(_("abort: %s\n") % inst)
54 54 if inst.hint:
55 55 ferr.write(_("(%s)\n") % inst.hint)
56 56 return -1
57 57 except error.ParseError, inst:
58 58 if len(inst.args) > 1:
59 59 ferr.write(_("hg: parse error at %s: %s\n") %
60 60 (inst.args[1], inst.args[0]))
61 61 else:
62 62 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
63 63 return -1
64 64
65 65 return _runcatch(req)
66 66
67 67 def _runcatch(req):
68 68 def catchterm(*args):
69 69 raise error.SignalInterrupt
70 70
71 71 ui = req.ui
72 72 try:
73 73 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
74 74 num = getattr(signal, name, None)
75 75 if num:
76 76 signal.signal(num, catchterm)
77 77 except ValueError:
78 78 pass # happens if called in a thread
79 79
80 80 try:
81 81 try:
82 82 # enter the debugger before command execution
83 83 if '--debugger' in req.args:
84 84 ui.warn(_("entering debugger - "
85 85 "type c to continue starting hg or h for help\n"))
86 86 pdb.set_trace()
87 87 try:
88 88 return _dispatch(req)
89 89 finally:
90 90 ui.flush()
91 91 except: # re-raises
92 92 # enter the debugger when we hit an exception
93 93 if '--debugger' in req.args:
94 94 traceback.print_exc()
95 95 pdb.post_mortem(sys.exc_info()[2])
96 96 ui.traceback()
97 97 raise
98 98
99 99 # Global exception handling, alphabetically
100 100 # Mercurial-specific first, followed by built-in and library exceptions
101 101 except error.AmbiguousCommand, inst:
102 102 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
103 103 (inst.args[0], " ".join(inst.args[1])))
104 104 except error.ParseError, inst:
105 105 if len(inst.args) > 1:
106 106 ui.warn(_("hg: parse error at %s: %s\n") %
107 107 (inst.args[1], inst.args[0]))
108 108 else:
109 109 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
110 110 return -1
111 111 except error.LockHeld, inst:
112 112 if inst.errno == errno.ETIMEDOUT:
113 113 reason = _('timed out waiting for lock held by %s') % inst.locker
114 114 else:
115 115 reason = _('lock held by %s') % inst.locker
116 116 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
117 117 except error.LockUnavailable, inst:
118 118 ui.warn(_("abort: could not lock %s: %s\n") %
119 119 (inst.desc or inst.filename, inst.strerror))
120 120 except error.CommandError, inst:
121 121 if inst.args[0]:
122 122 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
123 123 commands.help_(ui, inst.args[0], full=False, command=True)
124 124 else:
125 125 ui.warn(_("hg: %s\n") % inst.args[1])
126 126 commands.help_(ui, 'shortlist')
127 127 except error.OutOfBandError, inst:
128 128 ui.warn(_("abort: remote error:\n"))
129 129 ui.warn(''.join(inst.args))
130 130 except error.RepoError, inst:
131 131 ui.warn(_("abort: %s!\n") % inst)
132 132 if inst.hint:
133 133 ui.warn(_("(%s)\n") % inst.hint)
134 134 except error.ResponseError, inst:
135 135 ui.warn(_("abort: %s") % inst.args[0])
136 136 if not isinstance(inst.args[1], basestring):
137 137 ui.warn(" %r\n" % (inst.args[1],))
138 138 elif not inst.args[1]:
139 139 ui.warn(_(" empty string\n"))
140 140 else:
141 141 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
142 142 except error.RevlogError, inst:
143 143 ui.warn(_("abort: %s!\n") % inst)
144 144 except error.SignalInterrupt:
145 145 ui.warn(_("killed!\n"))
146 146 except error.UnknownCommand, inst:
147 147 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
148 148 try:
149 149 # check if the command is in a disabled extension
150 150 # (but don't check for extensions themselves)
151 151 commands.help_(ui, inst.args[0], unknowncmd=True)
152 152 except error.UnknownCommand:
153 153 commands.help_(ui, 'shortlist')
154 154 except util.Abort, inst:
155 155 ui.warn(_("abort: %s\n") % inst)
156 156 if inst.hint:
157 157 ui.warn(_("(%s)\n") % inst.hint)
158 158 except ImportError, inst:
159 159 ui.warn(_("abort: %s!\n") % inst)
160 160 m = str(inst).split()[-1]
161 161 if m in "mpatch bdiff".split():
162 162 ui.warn(_("(did you forget to compile extensions?)\n"))
163 163 elif m in "zlib".split():
164 164 ui.warn(_("(is your Python install correct?)\n"))
165 165 except IOError, inst:
166 166 if util.safehasattr(inst, "code"):
167 167 ui.warn(_("abort: %s\n") % inst)
168 168 elif util.safehasattr(inst, "reason"):
169 169 try: # usually it is in the form (errno, strerror)
170 170 reason = inst.reason.args[1]
171 171 except (AttributeError, IndexError):
172 172 # it might be anything, for example a string
173 173 reason = inst.reason
174 174 ui.warn(_("abort: error: %s\n") % reason)
175 175 elif util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
176 176 if ui.debugflag:
177 177 ui.warn(_("broken pipe\n"))
178 178 elif getattr(inst, "strerror", None):
179 179 if getattr(inst, "filename", None):
180 180 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
181 181 else:
182 182 ui.warn(_("abort: %s\n") % inst.strerror)
183 183 else:
184 184 raise
185 185 except OSError, inst:
186 if getattr(inst, "filename", None):
187 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
186 if getattr(inst, "filename", None) is not None:
187 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
188 188 else:
189 189 ui.warn(_("abort: %s\n") % inst.strerror)
190 190 except KeyboardInterrupt:
191 191 try:
192 192 ui.warn(_("interrupted!\n"))
193 193 except IOError, inst:
194 194 if inst.errno == errno.EPIPE:
195 195 if ui.debugflag:
196 196 ui.warn(_("\nbroken pipe\n"))
197 197 else:
198 198 raise
199 199 except MemoryError:
200 200 ui.warn(_("abort: out of memory\n"))
201 201 except SystemExit, inst:
202 202 # Commands shouldn't sys.exit directly, but give a return code.
203 203 # Just in case catch this and and pass exit code to caller.
204 204 return inst.code
205 205 except socket.error, inst:
206 206 ui.warn(_("abort: %s\n") % inst.args[-1])
207 207 except: # re-raises
208 208 myver = util.version()
209 209 # For compatibility checking, we discard the portion of the hg
210 210 # version after the + on the assumption that if a "normal
211 211 # user" is running a build with a + in it the packager
212 212 # probably built from fairly close to a tag and anyone with a
213 213 # 'make local' copy of hg (where the version number can be out
214 214 # of date) will be clueful enough to notice the implausible
215 215 # version number and try updating.
216 216 compare = myver.split('+')[0]
217 217 ct = tuplever(compare)
218 218 worst = None, ct, ''
219 219 for name, mod in extensions.extensions():
220 220 testedwith = getattr(mod, 'testedwith', '')
221 221 report = getattr(mod, 'buglink', _('the extension author.'))
222 222 if not testedwith.strip():
223 223 # We found an untested extension. It's likely the culprit.
224 224 worst = name, 'unknown', report
225 225 break
226 226 if compare not in testedwith.split() and testedwith != 'internal':
227 227 tested = [tuplever(v) for v in testedwith.split()]
228 228 lower = [t for t in tested if t < ct]
229 229 nearest = max(lower or tested)
230 230 if worst[0] is None or nearest < worst[1]:
231 231 worst = name, nearest, report
232 232 if worst[0] is not None:
233 233 name, testedwith, report = worst
234 234 if not isinstance(testedwith, str):
235 235 testedwith = '.'.join([str(c) for c in testedwith])
236 236 warning = (_('** Unknown exception encountered with '
237 237 'possibly-broken third-party extension %s\n'
238 238 '** which supports versions %s of Mercurial.\n'
239 239 '** Please disable %s and try your action again.\n'
240 240 '** If that fixes the bug please report it to %s\n')
241 241 % (name, testedwith, name, report))
242 242 else:
243 243 warning = (_("** unknown exception encountered, "
244 244 "please report by visiting\n") +
245 245 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
246 246 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
247 247 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
248 248 (_("** Extensions loaded: %s\n") %
249 249 ", ".join([x[0] for x in extensions.extensions()])))
250 250 ui.warn(warning)
251 251 raise
252 252
253 253 return -1
254 254
255 255 def tuplever(v):
256 256 try:
257 257 return tuple([int(i) for i in v.split('.')])
258 258 except ValueError:
259 259 return tuple()
260 260
261 261 def aliasargs(fn, givenargs):
262 262 args = getattr(fn, 'args', [])
263 263 if args:
264 264 cmd = ' '.join(map(util.shellquote, args))
265 265
266 266 nums = []
267 267 def replacer(m):
268 268 num = int(m.group(1)) - 1
269 269 nums.append(num)
270 270 if num < len(givenargs):
271 271 return givenargs[num]
272 272 raise util.Abort(_('too few arguments for command alias'))
273 273 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
274 274 givenargs = [x for i, x in enumerate(givenargs)
275 275 if i not in nums]
276 276 args = shlex.split(cmd)
277 277 return args + givenargs
278 278
279 279 class cmdalias(object):
280 280 def __init__(self, name, definition, cmdtable):
281 281 self.name = self.cmd = name
282 282 self.cmdname = ''
283 283 self.definition = definition
284 284 self.args = []
285 285 self.opts = []
286 286 self.help = ''
287 287 self.norepo = True
288 288 self.optionalrepo = False
289 289 self.badalias = False
290 290
291 291 try:
292 292 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
293 293 for alias, e in cmdtable.iteritems():
294 294 if e is entry:
295 295 self.cmd = alias
296 296 break
297 297 self.shadows = True
298 298 except error.UnknownCommand:
299 299 self.shadows = False
300 300
301 301 if not self.definition:
302 302 def fn(ui, *args):
303 303 ui.warn(_("no definition for alias '%s'\n") % self.name)
304 304 return 1
305 305 self.fn = fn
306 306 self.badalias = True
307 307 return
308 308
309 309 if self.definition.startswith('!'):
310 310 self.shell = True
311 311 def fn(ui, *args):
312 312 env = {'HG_ARGS': ' '.join((self.name,) + args)}
313 313 def _checkvar(m):
314 314 if m.groups()[0] == '$':
315 315 return m.group()
316 316 elif int(m.groups()[0]) <= len(args):
317 317 return m.group()
318 318 else:
319 319 ui.debug("No argument found for substitution "
320 320 "of %i variable in alias '%s' definition."
321 321 % (int(m.groups()[0]), self.name))
322 322 return ''
323 323 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
324 324 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
325 325 replace['0'] = self.name
326 326 replace['@'] = ' '.join(args)
327 327 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
328 328 return util.system(cmd, environ=env, out=ui.fout)
329 329 self.fn = fn
330 330 return
331 331
332 332 args = shlex.split(self.definition)
333 333 self.cmdname = cmd = args.pop(0)
334 334 args = map(util.expandpath, args)
335 335
336 336 for invalidarg in ("--cwd", "-R", "--repository", "--repo"):
337 337 if _earlygetopt([invalidarg], args):
338 338 def fn(ui, *args):
339 339 ui.warn(_("error in definition for alias '%s': %s may only "
340 340 "be given on the command line\n")
341 341 % (self.name, invalidarg))
342 342 return 1
343 343
344 344 self.fn = fn
345 345 self.badalias = True
346 346 return
347 347
348 348 try:
349 349 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
350 350 if len(tableentry) > 2:
351 351 self.fn, self.opts, self.help = tableentry
352 352 else:
353 353 self.fn, self.opts = tableentry
354 354
355 355 self.args = aliasargs(self.fn, args)
356 356 if cmd not in commands.norepo.split(' '):
357 357 self.norepo = False
358 358 if cmd in commands.optionalrepo.split(' '):
359 359 self.optionalrepo = True
360 360 if self.help.startswith("hg " + cmd):
361 361 # drop prefix in old-style help lines so hg shows the alias
362 362 self.help = self.help[4 + len(cmd):]
363 363 self.__doc__ = self.fn.__doc__
364 364
365 365 except error.UnknownCommand:
366 366 def fn(ui, *args):
367 367 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
368 368 % (self.name, cmd))
369 369 try:
370 370 # check if the command is in a disabled extension
371 371 commands.help_(ui, cmd, unknowncmd=True)
372 372 except error.UnknownCommand:
373 373 pass
374 374 return 1
375 375 self.fn = fn
376 376 self.badalias = True
377 377 except error.AmbiguousCommand:
378 378 def fn(ui, *args):
379 379 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
380 380 % (self.name, cmd))
381 381 return 1
382 382 self.fn = fn
383 383 self.badalias = True
384 384
385 385 def __call__(self, ui, *args, **opts):
386 386 if self.shadows:
387 387 ui.debug("alias '%s' shadows command '%s'\n" %
388 388 (self.name, self.cmdname))
389 389
390 390 if util.safehasattr(self, 'shell'):
391 391 return self.fn(ui, *args, **opts)
392 392 else:
393 393 try:
394 394 util.checksignature(self.fn)(ui, *args, **opts)
395 395 except error.SignatureError:
396 396 args = ' '.join([self.cmdname] + self.args)
397 397 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
398 398 raise
399 399
400 400 def addaliases(ui, cmdtable):
401 401 # aliases are processed after extensions have been loaded, so they
402 402 # may use extension commands. Aliases can also use other alias definitions,
403 403 # but only if they have been defined prior to the current definition.
404 404 for alias, definition in ui.configitems('alias'):
405 405 aliasdef = cmdalias(alias, definition, cmdtable)
406 406
407 407 try:
408 408 olddef = cmdtable[aliasdef.cmd][0]
409 409 if olddef.definition == aliasdef.definition:
410 410 continue
411 411 except (KeyError, AttributeError):
412 412 # definition might not exist or it might not be a cmdalias
413 413 pass
414 414
415 415 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
416 416 if aliasdef.norepo:
417 417 commands.norepo += ' %s' % alias
418 418 if aliasdef.optionalrepo:
419 419 commands.optionalrepo += ' %s' % alias
420 420
421 421 def _parse(ui, args):
422 422 options = {}
423 423 cmdoptions = {}
424 424
425 425 try:
426 426 args = fancyopts.fancyopts(args, commands.globalopts, options)
427 427 except fancyopts.getopt.GetoptError, inst:
428 428 raise error.CommandError(None, inst)
429 429
430 430 if args:
431 431 cmd, args = args[0], args[1:]
432 432 aliases, entry = cmdutil.findcmd(cmd, commands.table,
433 433 ui.configbool("ui", "strict"))
434 434 cmd = aliases[0]
435 435 args = aliasargs(entry[0], args)
436 436 defaults = ui.config("defaults", cmd)
437 437 if defaults:
438 438 args = map(util.expandpath, shlex.split(defaults)) + args
439 439 c = list(entry[1])
440 440 else:
441 441 cmd = None
442 442 c = []
443 443
444 444 # combine global options into local
445 445 for o in commands.globalopts:
446 446 c.append((o[0], o[1], options[o[1]], o[3]))
447 447
448 448 try:
449 449 args = fancyopts.fancyopts(args, c, cmdoptions, True)
450 450 except fancyopts.getopt.GetoptError, inst:
451 451 raise error.CommandError(cmd, inst)
452 452
453 453 # separate global options back out
454 454 for o in commands.globalopts:
455 455 n = o[1]
456 456 options[n] = cmdoptions[n]
457 457 del cmdoptions[n]
458 458
459 459 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
460 460
461 461 def _parseconfig(ui, config):
462 462 """parse the --config options from the command line"""
463 463 configs = []
464 464
465 465 for cfg in config:
466 466 try:
467 467 name, value = cfg.split('=', 1)
468 468 section, name = name.split('.', 1)
469 469 if not section or not name:
470 470 raise IndexError
471 471 ui.setconfig(section, name, value)
472 472 configs.append((section, name, value))
473 473 except (IndexError, ValueError):
474 474 raise util.Abort(_('malformed --config option: %r '
475 475 '(use --config section.name=value)') % cfg)
476 476
477 477 return configs
478 478
479 479 def _earlygetopt(aliases, args):
480 480 """Return list of values for an option (or aliases).
481 481
482 482 The values are listed in the order they appear in args.
483 483 The options and values are removed from args.
484 484 """
485 485 try:
486 486 argcount = args.index("--")
487 487 except ValueError:
488 488 argcount = len(args)
489 489 shortopts = [opt for opt in aliases if len(opt) == 2]
490 490 values = []
491 491 pos = 0
492 492 while pos < argcount:
493 493 if args[pos] in aliases:
494 494 if pos + 1 >= argcount:
495 495 # ignore and let getopt report an error if there is no value
496 496 break
497 497 del args[pos]
498 498 values.append(args.pop(pos))
499 499 argcount -= 2
500 500 elif args[pos][:2] in shortopts:
501 501 # short option can have no following space, e.g. hg log -Rfoo
502 502 values.append(args.pop(pos)[2:])
503 503 argcount -= 1
504 504 else:
505 505 pos += 1
506 506 return values
507 507
508 508 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
509 509 # run pre-hook, and abort if it fails
510 510 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
511 511 pats=cmdpats, opts=cmdoptions)
512 512 if ret:
513 513 return ret
514 514 ret = _runcommand(ui, options, cmd, d)
515 515 # run post-hook, passing command result
516 516 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
517 517 result=ret, pats=cmdpats, opts=cmdoptions)
518 518 return ret
519 519
520 520 def _getlocal(ui, rpath):
521 521 """Return (path, local ui object) for the given target path.
522 522
523 523 Takes paths in [cwd]/.hg/hgrc into account."
524 524 """
525 525 try:
526 526 wd = os.getcwd()
527 527 except OSError, e:
528 528 raise util.Abort(_("error getting current working directory: %s") %
529 529 e.strerror)
530 530 path = cmdutil.findrepo(wd) or ""
531 531 if not path:
532 532 lui = ui
533 533 else:
534 534 lui = ui.copy()
535 535 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
536 536
537 537 if rpath and rpath[-1]:
538 538 path = lui.expandpath(rpath[-1])
539 539 lui = ui.copy()
540 540 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
541 541
542 542 return path, lui
543 543
544 544 def _checkshellalias(lui, ui, args):
545 545 options = {}
546 546
547 547 try:
548 548 args = fancyopts.fancyopts(args, commands.globalopts, options)
549 549 except fancyopts.getopt.GetoptError:
550 550 return
551 551
552 552 if not args:
553 553 return
554 554
555 555 norepo = commands.norepo
556 556 optionalrepo = commands.optionalrepo
557 557 def restorecommands():
558 558 commands.norepo = norepo
559 559 commands.optionalrepo = optionalrepo
560 560
561 561 cmdtable = commands.table.copy()
562 562 addaliases(lui, cmdtable)
563 563
564 564 cmd = args[0]
565 565 try:
566 566 aliases, entry = cmdutil.findcmd(cmd, cmdtable,
567 567 lui.configbool("ui", "strict"))
568 568 except (error.AmbiguousCommand, error.UnknownCommand):
569 569 restorecommands()
570 570 return
571 571
572 572 cmd = aliases[0]
573 573 fn = entry[0]
574 574
575 575 if cmd and util.safehasattr(fn, 'shell'):
576 576 d = lambda: fn(ui, *args[1:])
577 577 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
578 578 [], {})
579 579
580 580 restorecommands()
581 581
582 582 _loaded = set()
583 583 def _dispatch(req):
584 584 args = req.args
585 585 ui = req.ui
586 586
587 587 # read --config before doing anything else
588 588 # (e.g. to change trust settings for reading .hg/hgrc)
589 589 cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
590 590
591 591 # check for cwd
592 592 cwd = _earlygetopt(['--cwd'], args)
593 593 if cwd:
594 594 os.chdir(cwd[-1])
595 595
596 596 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
597 597 path, lui = _getlocal(ui, rpath)
598 598
599 599 # Now that we're operating in the right directory/repository with
600 600 # the right config settings, check for shell aliases
601 601 shellaliasfn = _checkshellalias(lui, ui, args)
602 602 if shellaliasfn:
603 603 return shellaliasfn()
604 604
605 605 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
606 606 # reposetup. Programs like TortoiseHg will call _dispatch several
607 607 # times so we keep track of configured extensions in _loaded.
608 608 extensions.loadall(lui)
609 609 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
610 610 # Propagate any changes to lui.__class__ by extensions
611 611 ui.__class__ = lui.__class__
612 612
613 613 # (uisetup and extsetup are handled in extensions.loadall)
614 614
615 615 for name, module in exts:
616 616 cmdtable = getattr(module, 'cmdtable', {})
617 617 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
618 618 if overrides:
619 619 ui.warn(_("extension '%s' overrides commands: %s\n")
620 620 % (name, " ".join(overrides)))
621 621 commands.table.update(cmdtable)
622 622 _loaded.add(name)
623 623
624 624 # (reposetup is handled in hg.repository)
625 625
626 626 addaliases(lui, commands.table)
627 627
628 628 # check for fallback encoding
629 629 fallback = lui.config('ui', 'fallbackencoding')
630 630 if fallback:
631 631 encoding.fallbackencoding = fallback
632 632
633 633 fullargs = args
634 634 cmd, func, args, options, cmdoptions = _parse(lui, args)
635 635
636 636 if options["config"]:
637 637 raise util.Abort(_("option --config may not be abbreviated!"))
638 638 if options["cwd"]:
639 639 raise util.Abort(_("option --cwd may not be abbreviated!"))
640 640 if options["repository"]:
641 641 raise util.Abort(_(
642 642 "option -R has to be separated from other options (e.g. not -qR) "
643 643 "and --repository may only be abbreviated as --repo!"))
644 644
645 645 if options["encoding"]:
646 646 encoding.encoding = options["encoding"]
647 647 if options["encodingmode"]:
648 648 encoding.encodingmode = options["encodingmode"]
649 649 if options["time"]:
650 650 def get_times():
651 651 t = os.times()
652 652 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
653 653 t = (t[0], t[1], t[2], t[3], time.clock())
654 654 return t
655 655 s = get_times()
656 656 def print_time():
657 657 t = get_times()
658 658 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
659 659 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
660 660 atexit.register(print_time)
661 661
662 662 uis = set([ui, lui])
663 663
664 664 if req.repo:
665 665 uis.add(req.repo.ui)
666 666
667 667 # copy configs that were passed on the cmdline (--config) to the repo ui
668 668 for cfg in cfgs:
669 669 req.repo.ui.setconfig(*cfg)
670 670
671 671 if options['verbose'] or options['debug'] or options['quiet']:
672 672 for opt in ('verbose', 'debug', 'quiet'):
673 673 val = str(bool(options[opt]))
674 674 for ui_ in uis:
675 675 ui_.setconfig('ui', opt, val)
676 676
677 677 if options['traceback']:
678 678 for ui_ in uis:
679 679 ui_.setconfig('ui', 'traceback', 'on')
680 680
681 681 if options['noninteractive']:
682 682 for ui_ in uis:
683 683 ui_.setconfig('ui', 'interactive', 'off')
684 684
685 685 if cmdoptions.get('insecure', False):
686 686 for ui_ in uis:
687 687 ui_.setconfig('web', 'cacerts', '')
688 688
689 689 if options['version']:
690 690 return commands.version_(ui)
691 691 if options['help']:
692 692 return commands.help_(ui, cmd)
693 693 elif not cmd:
694 694 return commands.help_(ui, 'shortlist')
695 695
696 696 repo = None
697 697 cmdpats = args[:]
698 698 if cmd not in commands.norepo.split():
699 699 # use the repo from the request only if we don't have -R
700 700 if not rpath and not cwd:
701 701 repo = req.repo
702 702
703 703 if repo:
704 704 # set the descriptors of the repo ui to those of ui
705 705 repo.ui.fin = ui.fin
706 706 repo.ui.fout = ui.fout
707 707 repo.ui.ferr = ui.ferr
708 708 else:
709 709 try:
710 710 repo = hg.repository(ui, path=path)
711 711 if not repo.local():
712 712 raise util.Abort(_("repository '%s' is not local") % path)
713 713 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
714 714 except error.RequirementError:
715 715 raise
716 716 except error.RepoError:
717 717 if cmd not in commands.optionalrepo.split():
718 718 if (cmd in commands.inferrepo.split() and
719 719 args and not path): # try to infer -R from command args
720 720 repos = map(cmdutil.findrepo, args)
721 721 guess = repos[0]
722 722 if guess and repos.count(guess) == len(repos):
723 723 req.args = ['--repository', guess] + fullargs
724 724 return _dispatch(req)
725 725 if not path:
726 726 raise error.RepoError(_("no repository found in '%s'"
727 727 " (.hg not found)")
728 728 % os.getcwd())
729 729 raise
730 730 if repo:
731 731 ui = repo.ui
732 732 args.insert(0, repo)
733 733 elif rpath:
734 734 ui.warn(_("warning: --repository ignored\n"))
735 735
736 736 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
737 737 ui.log("command", msg + "\n")
738 738 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
739 739 try:
740 740 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
741 741 cmdpats, cmdoptions)
742 742 finally:
743 743 if repo and repo != req.repo:
744 744 repo.close()
745 745
746 746 def lsprofile(ui, func, fp):
747 747 format = ui.config('profiling', 'format', default='text')
748 748 field = ui.config('profiling', 'sort', default='inlinetime')
749 749 climit = ui.configint('profiling', 'nested', default=5)
750 750
751 751 if format not in ['text', 'kcachegrind']:
752 752 ui.warn(_("unrecognized profiling format '%s'"
753 753 " - Ignored\n") % format)
754 754 format = 'text'
755 755
756 756 try:
757 757 from mercurial import lsprof
758 758 except ImportError:
759 759 raise util.Abort(_(
760 760 'lsprof not available - install from '
761 761 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
762 762 p = lsprof.Profiler()
763 763 p.enable(subcalls=True)
764 764 try:
765 765 return func()
766 766 finally:
767 767 p.disable()
768 768
769 769 if format == 'kcachegrind':
770 770 import lsprofcalltree
771 771 calltree = lsprofcalltree.KCacheGrind(p)
772 772 calltree.output(fp)
773 773 else:
774 774 # format == 'text'
775 775 stats = lsprof.Stats(p.getstats())
776 776 stats.sort(field)
777 777 stats.pprint(limit=30, file=fp, climit=climit)
778 778
779 779 def statprofile(ui, func, fp):
780 780 try:
781 781 import statprof
782 782 except ImportError:
783 783 raise util.Abort(_(
784 784 'statprof not available - install using "easy_install statprof"'))
785 785
786 786 freq = ui.configint('profiling', 'freq', default=1000)
787 787 if freq > 0:
788 788 statprof.reset(freq)
789 789 else:
790 790 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
791 791
792 792 statprof.start()
793 793 try:
794 794 return func()
795 795 finally:
796 796 statprof.stop()
797 797 statprof.display(fp)
798 798
799 799 def _runcommand(ui, options, cmd, cmdfunc):
800 800 def checkargs():
801 801 try:
802 802 return cmdfunc()
803 803 except error.SignatureError:
804 804 raise error.CommandError(cmd, _("invalid arguments"))
805 805
806 806 if options['profile']:
807 807 profiler = os.getenv('HGPROF')
808 808 if profiler is None:
809 809 profiler = ui.config('profiling', 'type', default='ls')
810 810 if profiler not in ('ls', 'stat'):
811 811 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
812 812 profiler = 'ls'
813 813
814 814 output = ui.config('profiling', 'output')
815 815
816 816 if output:
817 817 path = ui.expandpath(output)
818 818 fp = open(path, 'wb')
819 819 else:
820 820 fp = sys.stderr
821 821
822 822 try:
823 823 if profiler == 'ls':
824 824 return lsprofile(ui, checkargs, fp)
825 825 else:
826 826 return statprofile(ui, checkargs, fp)
827 827 finally:
828 828 if output:
829 829 fp.close()
830 830 else:
831 831 return checkargs()
@@ -1,92 +1,92 b''
1 1 $ hg init
2 2
3 3 audit of .hg
4 4
5 5 $ hg add .hg/00changelog.i
6 6 abort: path contains illegal component: .hg/00changelog.i (glob)
7 7 [255]
8 8
9 9 #if symlink
10 10
11 11 Symlinks
12 12
13 13 $ mkdir a
14 14 $ echo a > a/a
15 15 $ hg ci -Ama
16 16 adding a/a
17 17 $ ln -s a b
18 18 $ echo b > a/b
19 19 $ hg add b/b
20 20 abort: path 'b/b' traverses symbolic link 'b' (glob)
21 21 [255]
22 22 $ hg add b
23 23
24 24 should still fail - maybe
25 25
26 26 $ hg add b/b
27 27 abort: path 'b/b' traverses symbolic link 'b' (glob)
28 28 [255]
29 29
30 30 #endif
31 31
32 32
33 33 unbundle tampered bundle
34 34
35 35 $ hg init target
36 36 $ cd target
37 37 $ hg unbundle "$TESTDIR/bundles/tampered.hg"
38 38 adding changesets
39 39 adding manifests
40 40 adding file changes
41 41 added 5 changesets with 6 changes to 6 files (+4 heads)
42 42 (run 'hg heads' to see heads, 'hg merge' to merge)
43 43
44 44 attack .hg/test
45 45
46 46 $ hg manifest -r0
47 47 .hg/test
48 48 $ hg update -Cr0
49 49 abort: path contains illegal component: .hg/test (glob)
50 50 [255]
51 51
52 52 attack foo/.hg/test
53 53
54 54 $ hg manifest -r1
55 55 foo/.hg/test
56 56 $ hg update -Cr1
57 57 abort: path 'foo/.hg/test' is inside nested repo 'foo' (glob)
58 58 [255]
59 59
60 60 attack back/test where back symlinks to ..
61 61
62 62 $ hg manifest -r2
63 63 back
64 64 back/test
65 65 #if symlink
66 66 $ hg update -Cr2
67 67 abort: path 'back/test' traverses symbolic link 'back'
68 68 [255]
69 69 #else
70 70 ('back' will be a file and cause some other system specific error)
71 71 $ hg update -Cr2
72 72 abort: * (glob)
73 73 [255]
74 74 #endif
75 75
76 76 attack ../test
77 77
78 78 $ hg manifest -r3
79 79 ../test
80 80 $ hg update -Cr3
81 81 abort: path contains illegal component: ../test (glob)
82 82 [255]
83 83
84 84 attack /tmp/test
85 85
86 86 $ hg manifest -r4
87 87 /tmp/test
88 88 $ hg update -Cr4
89 abort: *: $TESTTMP/target//tmp/test (glob)
89 abort: *: '$TESTTMP/target//tmp/test' (glob)
90 90 [255]
91 91
92 92 $ cd ..
@@ -1,623 +1,623 b''
1 1 Prepare repo a:
2 2
3 3 $ hg init a
4 4 $ cd a
5 5 $ echo a > a
6 6 $ hg add a
7 7 $ hg commit -m test
8 8 $ echo first line > b
9 9 $ hg add b
10 10
11 11 Create a non-inlined filelog:
12 12
13 13 $ python -c 'file("data1", "wb").write("".join("%s\n" % x for x in range(10000)))'
14 14 $ for j in 0 1 2 3 4 5 6 7 8 9; do
15 15 > cat data1 >> b
16 16 > hg commit -m test
17 17 > done
18 18
19 19 List files in store/data (should show a 'b.d'):
20 20
21 21 $ for i in .hg/store/data/*; do
22 22 > echo $i
23 23 > done
24 24 .hg/store/data/a.i
25 25 .hg/store/data/b.d
26 26 .hg/store/data/b.i
27 27
28 28 Default operation:
29 29
30 30 $ hg clone . ../b
31 31 updating to branch default
32 32 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
33 33 $ cd ../b
34 34 $ cat a
35 35 a
36 36 $ hg verify
37 37 checking changesets
38 38 checking manifests
39 39 crosschecking files in changesets and manifests
40 40 checking files
41 41 2 files, 11 changesets, 11 total revisions
42 42
43 43 Invalid dest '' must abort:
44 44
45 45 $ hg clone . ''
46 46 abort: empty destination path is not valid
47 47 [255]
48 48
49 49 No update, with debug option:
50 50
51 51 #if hardlink
52 52 $ hg --debug clone -U . ../c
53 53 linked 8 files
54 54 listing keys for "bookmarks"
55 55 #else
56 56 $ hg --debug clone -U . ../c
57 57 copied 8 files
58 58 listing keys for "bookmarks"
59 59 #endif
60 60 $ cd ../c
61 61 $ cat a 2>/dev/null || echo "a not present"
62 62 a not present
63 63 $ hg verify
64 64 checking changesets
65 65 checking manifests
66 66 crosschecking files in changesets and manifests
67 67 checking files
68 68 2 files, 11 changesets, 11 total revisions
69 69
70 70 Default destination:
71 71
72 72 $ mkdir ../d
73 73 $ cd ../d
74 74 $ hg clone ../a
75 75 destination directory: a
76 76 updating to branch default
77 77 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
78 78 $ cd a
79 79 $ hg cat a
80 80 a
81 81 $ cd ../..
82 82
83 83 Check that we drop the 'file:' from the path before writing the .hgrc:
84 84
85 85 $ hg clone file:a e
86 86 updating to branch default
87 87 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 88 $ grep 'file:' e/.hg/hgrc
89 89 [1]
90 90
91 91 Check that path aliases are expanded:
92 92
93 93 $ hg clone -q -U --config 'paths.foobar=a#0' foobar f
94 94 $ hg -R f showconfig paths.default
95 95 $TESTTMP/a#0 (glob)
96 96
97 97 Use --pull:
98 98
99 99 $ hg clone --pull a g
100 100 requesting all changes
101 101 adding changesets
102 102 adding manifests
103 103 adding file changes
104 104 added 11 changesets with 11 changes to 2 files
105 105 updating to branch default
106 106 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
107 107 $ hg -R g verify
108 108 checking changesets
109 109 checking manifests
110 110 crosschecking files in changesets and manifests
111 111 checking files
112 112 2 files, 11 changesets, 11 total revisions
113 113
114 114 Invalid dest '' with --pull must abort (issue2528):
115 115
116 116 $ hg clone --pull a ''
117 117 abort: empty destination path is not valid
118 118 [255]
119 119
120 120 Clone to '.':
121 121
122 122 $ mkdir h
123 123 $ cd h
124 124 $ hg clone ../a .
125 125 updating to branch default
126 126 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
127 127 $ cd ..
128 128
129 129
130 130 *** Tests for option -u ***
131 131
132 132 Adding some more history to repo a:
133 133
134 134 $ cd a
135 135 $ hg tag ref1
136 136 $ echo the quick brown fox >a
137 137 $ hg ci -m "hacked default"
138 138 $ hg up ref1
139 139 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
140 140 $ hg branch stable
141 141 marked working directory as branch stable
142 142 (branches are permanent and global, did you want a bookmark?)
143 143 $ echo some text >a
144 144 $ hg ci -m "starting branch stable"
145 145 $ hg tag ref2
146 146 $ echo some more text >a
147 147 $ hg ci -m "another change for branch stable"
148 148 $ hg up ref2
149 149 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
150 150 $ hg parents
151 151 changeset: 13:e8ece76546a6
152 152 branch: stable
153 153 tag: ref2
154 154 parent: 10:a7949464abda
155 155 user: test
156 156 date: Thu Jan 01 00:00:00 1970 +0000
157 157 summary: starting branch stable
158 158
159 159
160 160 Repo a has two heads:
161 161
162 162 $ hg heads
163 163 changeset: 15:0aae7cf88f0d
164 164 branch: stable
165 165 tag: tip
166 166 user: test
167 167 date: Thu Jan 01 00:00:00 1970 +0000
168 168 summary: another change for branch stable
169 169
170 170 changeset: 12:f21241060d6a
171 171 user: test
172 172 date: Thu Jan 01 00:00:00 1970 +0000
173 173 summary: hacked default
174 174
175 175
176 176 $ cd ..
177 177
178 178
179 179 Testing --noupdate with --updaterev (must abort):
180 180
181 181 $ hg clone --noupdate --updaterev 1 a ua
182 182 abort: cannot specify both --noupdate and --updaterev
183 183 [255]
184 184
185 185
186 186 Testing clone -u:
187 187
188 188 $ hg clone -u . a ua
189 189 updating to branch stable
190 190 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
191 191
192 192 Repo ua has both heads:
193 193
194 194 $ hg -R ua heads
195 195 changeset: 15:0aae7cf88f0d
196 196 branch: stable
197 197 tag: tip
198 198 user: test
199 199 date: Thu Jan 01 00:00:00 1970 +0000
200 200 summary: another change for branch stable
201 201
202 202 changeset: 12:f21241060d6a
203 203 user: test
204 204 date: Thu Jan 01 00:00:00 1970 +0000
205 205 summary: hacked default
206 206
207 207
208 208 Same revision checked out in repo a and ua:
209 209
210 210 $ hg -R a parents --template "{node|short}\n"
211 211 e8ece76546a6
212 212 $ hg -R ua parents --template "{node|short}\n"
213 213 e8ece76546a6
214 214
215 215 $ rm -r ua
216 216
217 217
218 218 Testing clone --pull -u:
219 219
220 220 $ hg clone --pull -u . a ua
221 221 requesting all changes
222 222 adding changesets
223 223 adding manifests
224 224 adding file changes
225 225 added 16 changesets with 16 changes to 3 files (+1 heads)
226 226 updating to branch stable
227 227 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
228 228
229 229 Repo ua has both heads:
230 230
231 231 $ hg -R ua heads
232 232 changeset: 15:0aae7cf88f0d
233 233 branch: stable
234 234 tag: tip
235 235 user: test
236 236 date: Thu Jan 01 00:00:00 1970 +0000
237 237 summary: another change for branch stable
238 238
239 239 changeset: 12:f21241060d6a
240 240 user: test
241 241 date: Thu Jan 01 00:00:00 1970 +0000
242 242 summary: hacked default
243 243
244 244
245 245 Same revision checked out in repo a and ua:
246 246
247 247 $ hg -R a parents --template "{node|short}\n"
248 248 e8ece76546a6
249 249 $ hg -R ua parents --template "{node|short}\n"
250 250 e8ece76546a6
251 251
252 252 $ rm -r ua
253 253
254 254
255 255 Testing clone -u <branch>:
256 256
257 257 $ hg clone -u stable a ua
258 258 updating to branch stable
259 259 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
260 260
261 261 Repo ua has both heads:
262 262
263 263 $ hg -R ua heads
264 264 changeset: 15:0aae7cf88f0d
265 265 branch: stable
266 266 tag: tip
267 267 user: test
268 268 date: Thu Jan 01 00:00:00 1970 +0000
269 269 summary: another change for branch stable
270 270
271 271 changeset: 12:f21241060d6a
272 272 user: test
273 273 date: Thu Jan 01 00:00:00 1970 +0000
274 274 summary: hacked default
275 275
276 276
277 277 Branch 'stable' is checked out:
278 278
279 279 $ hg -R ua parents
280 280 changeset: 15:0aae7cf88f0d
281 281 branch: stable
282 282 tag: tip
283 283 user: test
284 284 date: Thu Jan 01 00:00:00 1970 +0000
285 285 summary: another change for branch stable
286 286
287 287
288 288 $ rm -r ua
289 289
290 290
291 291 Testing default checkout:
292 292
293 293 $ hg clone a ua
294 294 updating to branch default
295 295 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
296 296
297 297 Repo ua has both heads:
298 298
299 299 $ hg -R ua heads
300 300 changeset: 15:0aae7cf88f0d
301 301 branch: stable
302 302 tag: tip
303 303 user: test
304 304 date: Thu Jan 01 00:00:00 1970 +0000
305 305 summary: another change for branch stable
306 306
307 307 changeset: 12:f21241060d6a
308 308 user: test
309 309 date: Thu Jan 01 00:00:00 1970 +0000
310 310 summary: hacked default
311 311
312 312
313 313 Branch 'default' is checked out:
314 314
315 315 $ hg -R ua parents
316 316 changeset: 12:f21241060d6a
317 317 user: test
318 318 date: Thu Jan 01 00:00:00 1970 +0000
319 319 summary: hacked default
320 320
321 321 Test clone with a branch named "@" (issue3677)
322 322
323 323 $ hg -R ua branch @
324 324 marked working directory as branch @
325 325 (branches are permanent and global, did you want a bookmark?)
326 326 $ hg -R ua commit -m 'created branch @'
327 327 $ hg clone ua atbranch
328 328 updating to branch default
329 329 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
330 330 $ hg -R atbranch heads
331 331 changeset: 16:798b6d97153e
332 332 branch: @
333 333 tag: tip
334 334 parent: 12:f21241060d6a
335 335 user: test
336 336 date: Thu Jan 01 00:00:00 1970 +0000
337 337 summary: created branch @
338 338
339 339 changeset: 15:0aae7cf88f0d
340 340 branch: stable
341 341 user: test
342 342 date: Thu Jan 01 00:00:00 1970 +0000
343 343 summary: another change for branch stable
344 344
345 345 changeset: 12:f21241060d6a
346 346 user: test
347 347 date: Thu Jan 01 00:00:00 1970 +0000
348 348 summary: hacked default
349 349
350 350 $ hg -R atbranch parents
351 351 changeset: 12:f21241060d6a
352 352 user: test
353 353 date: Thu Jan 01 00:00:00 1970 +0000
354 354 summary: hacked default
355 355
356 356
357 357 $ rm -r ua atbranch
358 358
359 359
360 360 Testing #<branch>:
361 361
362 362 $ hg clone -u . a#stable ua
363 363 adding changesets
364 364 adding manifests
365 365 adding file changes
366 366 added 14 changesets with 14 changes to 3 files
367 367 updating to branch stable
368 368 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
369 369
370 370 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
371 371
372 372 $ hg -R ua heads
373 373 changeset: 13:0aae7cf88f0d
374 374 branch: stable
375 375 tag: tip
376 376 user: test
377 377 date: Thu Jan 01 00:00:00 1970 +0000
378 378 summary: another change for branch stable
379 379
380 380 changeset: 10:a7949464abda
381 381 user: test
382 382 date: Thu Jan 01 00:00:00 1970 +0000
383 383 summary: test
384 384
385 385
386 386 Same revision checked out in repo a and ua:
387 387
388 388 $ hg -R a parents --template "{node|short}\n"
389 389 e8ece76546a6
390 390 $ hg -R ua parents --template "{node|short}\n"
391 391 e8ece76546a6
392 392
393 393 $ rm -r ua
394 394
395 395
396 396 Testing -u -r <branch>:
397 397
398 398 $ hg clone -u . -r stable a ua
399 399 adding changesets
400 400 adding manifests
401 401 adding file changes
402 402 added 14 changesets with 14 changes to 3 files
403 403 updating to branch stable
404 404 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
405 405
406 406 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
407 407
408 408 $ hg -R ua heads
409 409 changeset: 13:0aae7cf88f0d
410 410 branch: stable
411 411 tag: tip
412 412 user: test
413 413 date: Thu Jan 01 00:00:00 1970 +0000
414 414 summary: another change for branch stable
415 415
416 416 changeset: 10:a7949464abda
417 417 user: test
418 418 date: Thu Jan 01 00:00:00 1970 +0000
419 419 summary: test
420 420
421 421
422 422 Same revision checked out in repo a and ua:
423 423
424 424 $ hg -R a parents --template "{node|short}\n"
425 425 e8ece76546a6
426 426 $ hg -R ua parents --template "{node|short}\n"
427 427 e8ece76546a6
428 428
429 429 $ rm -r ua
430 430
431 431
432 432 Testing -r <branch>:
433 433
434 434 $ hg clone -r stable a ua
435 435 adding changesets
436 436 adding manifests
437 437 adding file changes
438 438 added 14 changesets with 14 changes to 3 files
439 439 updating to branch stable
440 440 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
441 441
442 442 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
443 443
444 444 $ hg -R ua heads
445 445 changeset: 13:0aae7cf88f0d
446 446 branch: stable
447 447 tag: tip
448 448 user: test
449 449 date: Thu Jan 01 00:00:00 1970 +0000
450 450 summary: another change for branch stable
451 451
452 452 changeset: 10:a7949464abda
453 453 user: test
454 454 date: Thu Jan 01 00:00:00 1970 +0000
455 455 summary: test
456 456
457 457
458 458 Branch 'stable' is checked out:
459 459
460 460 $ hg -R ua parents
461 461 changeset: 13:0aae7cf88f0d
462 462 branch: stable
463 463 tag: tip
464 464 user: test
465 465 date: Thu Jan 01 00:00:00 1970 +0000
466 466 summary: another change for branch stable
467 467
468 468
469 469 $ rm -r ua
470 470
471 471
472 472 Issue2267: Error in 1.6 hg.py: TypeError: 'NoneType' object is not
473 473 iterable in addbranchrevs()
474 474
475 475 $ cat <<EOF > simpleclone.py
476 476 > from mercurial import ui, hg
477 477 > myui = ui.ui()
478 478 > repo = hg.repository(myui, 'a')
479 479 > hg.clone(myui, {}, repo, dest="ua")
480 480 > EOF
481 481
482 482 $ python simpleclone.py
483 483 updating to branch default
484 484 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
485 485
486 486 $ rm -r ua
487 487
488 488 $ cat <<EOF > branchclone.py
489 489 > from mercurial import ui, hg, extensions
490 490 > myui = ui.ui()
491 491 > extensions.loadall(myui)
492 492 > repo = hg.repository(myui, 'a')
493 493 > hg.clone(myui, {}, repo, dest="ua", branch=["stable",])
494 494 > EOF
495 495
496 496 $ python branchclone.py
497 497 adding changesets
498 498 adding manifests
499 499 adding file changes
500 500 added 14 changesets with 14 changes to 3 files
501 501 updating to branch stable
502 502 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
503 503 $ rm -r ua
504 504
505 505
506 506 Test clone with special '@' bookmark:
507 507 $ cd a
508 508 $ hg bookmark -r a7949464abda @ # branch point of stable from default
509 509 $ hg clone . ../i
510 510 updating to bookmark @
511 511 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
512 512 $ hg id -i ../i
513 513 a7949464abda
514 514 $ rm -r ../i
515 515
516 516 $ hg bookmark -f -r stable @
517 517 $ hg bookmarks
518 518 @ 15:0aae7cf88f0d
519 519 $ hg clone . ../i
520 520 updating to bookmark @ on branch stable
521 521 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
522 522 $ hg id -i ../i
523 523 0aae7cf88f0d
524 524 $ cd "$TESTTMP"
525 525
526 526
527 527 Testing failures:
528 528
529 529 $ mkdir fail
530 530 $ cd fail
531 531
532 532 No local source
533 533
534 534 $ hg clone a b
535 535 abort: repository a not found!
536 536 [255]
537 537
538 538 No remote source
539 539
540 540 $ hg clone http://127.0.0.1:3121/a b
541 541 abort: error: *refused* (glob)
542 542 [255]
543 543 $ rm -rf b # work around bug with http clone
544 544
545 545
546 546 #if unix-permissions
547 547
548 548 Inaccessible source
549 549
550 550 $ mkdir a
551 551 $ chmod 000 a
552 552 $ hg clone a b
553 553 abort: repository a not found!
554 554 [255]
555 555
556 556 Inaccessible destination
557 557
558 558 $ hg init b
559 559 $ cd b
560 560 $ hg clone . ../a
561 abort: Permission denied: ../a
561 abort: Permission denied: '../a'
562 562 [255]
563 563 $ cd ..
564 564 $ chmod 700 a
565 565 $ rm -r a b
566 566
567 567 #endif
568 568
569 569
570 570 #if fifo
571 571
572 572 Source of wrong type
573 573
574 574 $ mkfifo a
575 575 $ hg clone a b
576 576 abort: repository a not found!
577 577 [255]
578 578 $ rm a
579 579
580 580 #endif
581 581
582 582 Default destination, same directory
583 583
584 584 $ hg init q
585 585 $ hg clone q
586 586 destination directory: q
587 587 abort: destination 'q' is not empty
588 588 [255]
589 589
590 590 destination directory not empty
591 591
592 592 $ mkdir a
593 593 $ echo stuff > a/a
594 594 $ hg clone q a
595 595 abort: destination 'a' is not empty
596 596 [255]
597 597
598 598
599 599 #if unix-permissions
600 600
601 601 leave existing directory in place after clone failure
602 602
603 603 $ hg init c
604 604 $ cd c
605 605 $ echo c > c
606 606 $ hg commit -A -m test
607 607 adding c
608 608 $ chmod -rx .hg/store/data
609 609 $ cd ..
610 610 $ mkdir d
611 611 $ hg clone c d 2> err
612 612 [255]
613 613 $ test -d d
614 614 $ test -d d/.hg
615 615 [1]
616 616
617 617 re-enable perm to allow deletion
618 618
619 619 $ chmod +rx c/.hg/store/data
620 620
621 621 #endif
622 622
623 623 $ cd ..
@@ -1,455 +1,455 b''
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extensions]
3 3 > convert=
4 4 > [convert]
5 5 > hg.saverev=False
6 6 > EOF
7 7 $ hg help convert
8 8 hg convert [OPTION]... SOURCE [DEST [REVMAP]]
9 9
10 10 convert a foreign SCM repository to a Mercurial one.
11 11
12 12 Accepted source formats [identifiers]:
13 13
14 14 - Mercurial [hg]
15 15 - CVS [cvs]
16 16 - Darcs [darcs]
17 17 - git [git]
18 18 - Subversion [svn]
19 19 - Monotone [mtn]
20 20 - GNU Arch [gnuarch]
21 21 - Bazaar [bzr]
22 22 - Perforce [p4]
23 23
24 24 Accepted destination formats [identifiers]:
25 25
26 26 - Mercurial [hg]
27 27 - Subversion [svn] (history on branches is not preserved)
28 28
29 29 If no revision is given, all revisions will be converted. Otherwise,
30 30 convert will only import up to the named revision (given in a format
31 31 understood by the source).
32 32
33 33 If no destination directory name is specified, it defaults to the basename
34 34 of the source with "-hg" appended. If the destination repository doesn't
35 35 exist, it will be created.
36 36
37 37 By default, all sources except Mercurial will use --branchsort. Mercurial
38 38 uses --sourcesort to preserve original revision numbers order. Sort modes
39 39 have the following effects:
40 40
41 41 --branchsort convert from parent to child revision when possible, which
42 42 means branches are usually converted one after the other.
43 43 It generates more compact repositories.
44 44 --datesort sort revisions by date. Converted repositories have good-
45 45 looking changelogs but are often an order of magnitude
46 46 larger than the same ones generated by --branchsort.
47 47 --sourcesort try to preserve source revisions order, only supported by
48 48 Mercurial sources.
49 49
50 50 If "REVMAP" isn't given, it will be put in a default location
51 51 ("<dest>/.hg/shamap" by default). The "REVMAP" is a simple text file that
52 52 maps each source commit ID to the destination ID for that revision, like
53 53 so:
54 54
55 55 <source ID> <destination ID>
56 56
57 57 If the file doesn't exist, it's automatically created. It's updated on
58 58 each commit copied, so "hg convert" can be interrupted and can be run
59 59 repeatedly to copy new commits.
60 60
61 61 The authormap is a simple text file that maps each source commit author to
62 62 a destination commit author. It is handy for source SCMs that use unix
63 63 logins to identify authors (e.g.: CVS). One line per author mapping and
64 64 the line format is:
65 65
66 66 source author = destination author
67 67
68 68 Empty lines and lines starting with a "#" are ignored.
69 69
70 70 The filemap is a file that allows filtering and remapping of files and
71 71 directories. Each line can contain one of the following directives:
72 72
73 73 include path/to/file-or-dir
74 74
75 75 exclude path/to/file-or-dir
76 76
77 77 rename path/to/source path/to/destination
78 78
79 79 Comment lines start with "#". A specified path matches if it equals the
80 80 full relative name of a file or one of its parent directories. The
81 81 "include" or "exclude" directive with the longest matching path applies,
82 82 so line order does not matter.
83 83
84 84 The "include" directive causes a file, or all files under a directory, to
85 85 be included in the destination repository, and the exclusion of all other
86 86 files and directories not explicitly included. The "exclude" directive
87 87 causes files or directories to be omitted. The "rename" directive renames
88 88 a file or directory if it is converted. To rename from a subdirectory into
89 89 the root of the repository, use "." as the path to rename to.
90 90
91 91 The splicemap is a file that allows insertion of synthetic history,
92 92 letting you specify the parents of a revision. This is useful if you want
93 93 to e.g. give a Subversion merge two parents, or graft two disconnected
94 94 series of history together. Each entry contains a key, followed by a
95 95 space, followed by one or two comma-separated values:
96 96
97 97 key parent1, parent2
98 98
99 99 The key is the revision ID in the source revision control system whose
100 100 parents should be modified (same format as a key in .hg/shamap). The
101 101 values are the revision IDs (in either the source or destination revision
102 102 control system) that should be used as the new parents for that node. For
103 103 example, if you have merged "release-1.0" into "trunk", then you should
104 104 specify the revision on "trunk" as the first parent and the one on the
105 105 "release-1.0" branch as the second.
106 106
107 107 The branchmap is a file that allows you to rename a branch when it is
108 108 being brought in from whatever external repository. When used in
109 109 conjunction with a splicemap, it allows for a powerful combination to help
110 110 fix even the most badly mismanaged repositories and turn them into nicely
111 111 structured Mercurial repositories. The branchmap contains lines of the
112 112 form:
113 113
114 114 original_branch_name new_branch_name
115 115
116 116 where "original_branch_name" is the name of the branch in the source
117 117 repository, and "new_branch_name" is the name of the branch is the
118 118 destination repository. No whitespace is allowed in the branch names. This
119 119 can be used to (for instance) move code in one repository from "default"
120 120 to a named branch.
121 121
122 122 Mercurial Source
123 123 ################
124 124
125 125 The Mercurial source recognizes the following configuration options, which
126 126 you can set on the command line with "--config":
127 127
128 128 convert.hg.ignoreerrors
129 129 ignore integrity errors when reading. Use it to fix
130 130 Mercurial repositories with missing revlogs, by converting
131 131 from and to Mercurial. Default is False.
132 132 convert.hg.saverev
133 133 store original revision ID in changeset (forces target IDs
134 134 to change). It takes a boolean argument and defaults to
135 135 False.
136 136 convert.hg.startrev
137 137 convert start revision and its descendants. It takes a hg
138 138 revision identifier and defaults to 0.
139 139
140 140 CVS Source
141 141 ##########
142 142
143 143 CVS source will use a sandbox (i.e. a checked-out copy) from CVS to
144 144 indicate the starting point of what will be converted. Direct access to
145 145 the repository files is not needed, unless of course the repository is
146 146 ":local:". The conversion uses the top level directory in the sandbox to
147 147 find the CVS repository, and then uses CVS rlog commands to find files to
148 148 convert. This means that unless a filemap is given, all files under the
149 149 starting directory will be converted, and that any directory
150 150 reorganization in the CVS sandbox is ignored.
151 151
152 152 The following options can be used with "--config":
153 153
154 154 convert.cvsps.cache
155 155 Set to False to disable remote log caching, for testing and
156 156 debugging purposes. Default is True.
157 157 convert.cvsps.fuzz
158 158 Specify the maximum time (in seconds) that is allowed
159 159 between commits with identical user and log message in a
160 160 single changeset. When very large files were checked in as
161 161 part of a changeset then the default may not be long enough.
162 162 The default is 60.
163 163 convert.cvsps.mergeto
164 164 Specify a regular expression to which commit log messages
165 165 are matched. If a match occurs, then the conversion process
166 166 will insert a dummy revision merging the branch on which
167 167 this log message occurs to the branch indicated in the
168 168 regex. Default is "{{mergetobranch ([-\w]+)}}"
169 169 convert.cvsps.mergefrom
170 170 Specify a regular expression to which commit log messages
171 171 are matched. If a match occurs, then the conversion process
172 172 will add the most recent revision on the branch indicated in
173 173 the regex as the second parent of the changeset. Default is
174 174 "{{mergefrombranch ([-\w]+)}}"
175 175 convert.localtimezone
176 176 use local time (as determined by the TZ environment
177 177 variable) for changeset date/times. The default is False
178 178 (use UTC).
179 179 hook.cvslog Specify a Python function to be called at the end of
180 180 gathering the CVS log. The function is passed a list with
181 181 the log entries, and can modify the entries in-place, or add
182 182 or delete them.
183 183 hook.cvschangesets
184 184 Specify a Python function to be called after the changesets
185 185 are calculated from the CVS log. The function is passed a
186 186 list with the changeset entries, and can modify the
187 187 changesets in-place, or add or delete them.
188 188
189 189 An additional "debugcvsps" Mercurial command allows the builtin changeset
190 190 merging code to be run without doing a conversion. Its parameters and
191 191 output are similar to that of cvsps 2.1. Please see the command help for
192 192 more details.
193 193
194 194 Subversion Source
195 195 #################
196 196
197 197 Subversion source detects classical trunk/branches/tags layouts. By
198 198 default, the supplied "svn://repo/path/" source URL is converted as a
199 199 single branch. If "svn://repo/path/trunk" exists it replaces the default
200 200 branch. If "svn://repo/path/branches" exists, its subdirectories are
201 201 listed as possible branches. If "svn://repo/path/tags" exists, it is
202 202 looked for tags referencing converted branches. Default "trunk",
203 203 "branches" and "tags" values can be overridden with following options. Set
204 204 them to paths relative to the source URL, or leave them blank to disable
205 205 auto detection.
206 206
207 207 The following options can be set with "--config":
208 208
209 209 convert.svn.branches
210 210 specify the directory containing branches. The default is
211 211 "branches".
212 212 convert.svn.tags
213 213 specify the directory containing tags. The default is
214 214 "tags".
215 215 convert.svn.trunk
216 216 specify the name of the trunk branch. The default is
217 217 "trunk".
218 218 convert.localtimezone
219 219 use local time (as determined by the TZ environment
220 220 variable) for changeset date/times. The default is False
221 221 (use UTC).
222 222
223 223 Source history can be retrieved starting at a specific revision, instead
224 224 of being integrally converted. Only single branch conversions are
225 225 supported.
226 226
227 227 convert.svn.startrev
228 228 specify start Subversion revision number. The default is 0.
229 229
230 230 Perforce Source
231 231 ###############
232 232
233 233 The Perforce (P4) importer can be given a p4 depot path or a client
234 234 specification as source. It will convert all files in the source to a flat
235 235 Mercurial repository, ignoring labels, branches and integrations. Note
236 236 that when a depot path is given you then usually should specify a target
237 237 directory, because otherwise the target may be named "...-hg".
238 238
239 239 It is possible to limit the amount of source history to be converted by
240 240 specifying an initial Perforce revision:
241 241
242 242 convert.p4.startrev
243 243 specify initial Perforce revision (a Perforce changelist
244 244 number).
245 245
246 246 Mercurial Destination
247 247 #####################
248 248
249 249 The following options are supported:
250 250
251 251 convert.hg.clonebranches
252 252 dispatch source branches in separate clones. The default is
253 253 False.
254 254 convert.hg.tagsbranch
255 255 branch name for tag revisions, defaults to "default".
256 256 convert.hg.usebranchnames
257 257 preserve branch names. The default is True.
258 258
259 259 options:
260 260
261 261 -s --source-type TYPE source repository type
262 262 -d --dest-type TYPE destination repository type
263 263 -r --rev REV import up to target revision REV
264 264 -A --authormap FILE remap usernames using this file
265 265 --filemap FILE remap file names using contents of file
266 266 --splicemap FILE splice synthesized history into place
267 267 --branchmap FILE change branch names while converting
268 268 --branchsort try to sort changesets by branches
269 269 --datesort try to sort changesets by date
270 270 --sourcesort preserve source changesets order
271 271
272 272 use "hg -v help convert" to show the global options
273 273 $ hg init a
274 274 $ cd a
275 275 $ echo a > a
276 276 $ hg ci -d'0 0' -Ama
277 277 adding a
278 278 $ hg cp a b
279 279 $ hg ci -d'1 0' -mb
280 280 $ hg rm a
281 281 $ hg ci -d'2 0' -mc
282 282 $ hg mv b a
283 283 $ hg ci -d'3 0' -md
284 284 $ echo a >> a
285 285 $ hg ci -d'4 0' -me
286 286 $ cd ..
287 287 $ hg convert a 2>&1 | grep -v 'subversion python bindings could not be loaded'
288 288 assuming destination a-hg
289 289 initializing destination a-hg repository
290 290 scanning source...
291 291 sorting...
292 292 converting...
293 293 4 a
294 294 3 b
295 295 2 c
296 296 1 d
297 297 0 e
298 298 $ hg --cwd a-hg pull ../a
299 299 pulling from ../a
300 300 searching for changes
301 301 no changes found
302 302
303 303 conversion to existing file should fail
304 304
305 305 $ touch bogusfile
306 306 $ hg convert a bogusfile
307 307 initializing destination bogusfile repository
308 308 abort: cannot create new bundle repository
309 309 [255]
310 310
311 311 #if unix-permissions
312 312
313 313 conversion to dir without permissions should fail
314 314
315 315 $ mkdir bogusdir
316 316 $ chmod 000 bogusdir
317 317
318 318 $ hg convert a bogusdir
319 abort: Permission denied: bogusdir
319 abort: Permission denied: 'bogusdir'
320 320 [255]
321 321
322 322 user permissions should succeed
323 323
324 324 $ chmod 700 bogusdir
325 325 $ hg convert a bogusdir
326 326 initializing destination bogusdir repository
327 327 scanning source...
328 328 sorting...
329 329 converting...
330 330 4 a
331 331 3 b
332 332 2 c
333 333 1 d
334 334 0 e
335 335
336 336 #endif
337 337
338 338 test pre and post conversion actions
339 339
340 340 $ echo 'include b' > filemap
341 341 $ hg convert --debug --filemap filemap a partialb | \
342 342 > grep 'run hg'
343 343 run hg source pre-conversion action
344 344 run hg sink pre-conversion action
345 345 run hg sink post-conversion action
346 346 run hg source post-conversion action
347 347
348 348 converting empty dir should fail "nicely
349 349
350 350 $ mkdir emptydir
351 351
352 352 override $PATH to ensure p4 not visible; use $PYTHON in case we're
353 353 running from a devel copy, not a temp installation
354 354
355 355 $ PATH="$BINDIR" $PYTHON "$BINDIR"/hg convert emptydir
356 356 assuming destination emptydir-hg
357 357 initializing destination emptydir-hg repository
358 358 emptydir does not look like a CVS checkout
359 359 emptydir does not look like a Git repository
360 360 emptydir does not look like a Subversion repository
361 361 emptydir is not a local Mercurial repository
362 362 emptydir does not look like a darcs repository
363 363 emptydir does not look like a monotone repository
364 364 emptydir does not look like a GNU Arch repository
365 365 emptydir does not look like a Bazaar repository
366 366 cannot find required "p4" tool
367 367 abort: emptydir: missing or unsupported repository
368 368 [255]
369 369
370 370 convert with imaginary source type
371 371
372 372 $ hg convert --source-type foo a a-foo
373 373 initializing destination a-foo repository
374 374 abort: foo: invalid source repository type
375 375 [255]
376 376
377 377 convert with imaginary sink type
378 378
379 379 $ hg convert --dest-type foo a a-foo
380 380 abort: foo: invalid destination repository type
381 381 [255]
382 382
383 383 testing: convert must not produce duplicate entries in fncache
384 384
385 385 $ hg convert a b
386 386 initializing destination b repository
387 387 scanning source...
388 388 sorting...
389 389 converting...
390 390 4 a
391 391 3 b
392 392 2 c
393 393 1 d
394 394 0 e
395 395
396 396 contents of fncache file:
397 397
398 398 $ cat b/.hg/store/fncache | sort
399 399 data/a.i
400 400 data/b.i
401 401
402 402 test bogus URL
403 403
404 404 $ hg convert -q bzr+ssh://foobar@selenic.com/baz baz
405 405 abort: bzr+ssh://foobar@selenic.com/baz: missing or unsupported repository
406 406 [255]
407 407
408 408 test revset converted() lookup
409 409
410 410 $ hg --config convert.hg.saverev=True convert a c
411 411 initializing destination c repository
412 412 scanning source...
413 413 sorting...
414 414 converting...
415 415 4 a
416 416 3 b
417 417 2 c
418 418 1 d
419 419 0 e
420 420 $ echo f > c/f
421 421 $ hg -R c ci -d'0 0' -Amf
422 422 adding f
423 423 created new head
424 424 $ hg -R c log -r "converted(09d945a62ce6)"
425 425 changeset: 1:98c3dd46a874
426 426 user: test
427 427 date: Thu Jan 01 00:00:01 1970 +0000
428 428 summary: b
429 429
430 430 $ hg -R c log -r "converted()"
431 431 changeset: 0:31ed57b2037c
432 432 user: test
433 433 date: Thu Jan 01 00:00:00 1970 +0000
434 434 summary: a
435 435
436 436 changeset: 1:98c3dd46a874
437 437 user: test
438 438 date: Thu Jan 01 00:00:01 1970 +0000
439 439 summary: b
440 440
441 441 changeset: 2:3b9ca06ef716
442 442 user: test
443 443 date: Thu Jan 01 00:00:02 1970 +0000
444 444 summary: c
445 445
446 446 changeset: 3:4e0debd37cf2
447 447 user: test
448 448 date: Thu Jan 01 00:00:03 1970 +0000
449 449 summary: d
450 450
451 451 changeset: 4:9de3bc9349c5
452 452 user: test
453 453 date: Thu Jan 01 00:00:04 1970 +0000
454 454 summary: e
455 455
@@ -1,57 +1,63 b''
1 1 test command parsing and dispatch
2 2
3 3 $ hg init a
4 4 $ cd a
5 5
6 6 Redundant options used to crash (issue436):
7 7 $ hg -v log -v
8 8 $ hg -v log -v x
9 9
10 10 $ echo a > a
11 11 $ hg ci -Ama
12 12 adding a
13 13
14 14 Missing arg:
15 15
16 16 $ hg cat
17 17 hg cat: invalid arguments
18 18 hg cat [OPTION]... FILE...
19 19
20 20 output the current or given revision of files
21 21
22 22 options:
23 23
24 24 -o --output FORMAT print output to file with formatted name
25 25 -r --rev REV print the given revision
26 26 --decode apply any matching decode filter
27 27 -I --include PATTERN [+] include names matching the given patterns
28 28 -X --exclude PATTERN [+] exclude names matching the given patterns
29 29
30 30 [+] marked option can be specified multiple times
31 31
32 32 use "hg help cat" to show the full help text
33 33 [255]
34 34
35 35 [defaults]
36 36
37 37 $ hg cat a
38 38 a
39 39 $ cat >> $HGRCPATH <<EOF
40 40 > [defaults]
41 41 > cat = -r null
42 42 > EOF
43 43 $ hg cat a
44 44 a: no such file in rev 000000000000
45 45 [1]
46 46
47 47 $ cd "$TESTTMP"
48 48
49 OSError ... and with filename even when it is empty
50
51 $ hg -R a archive ''
52 abort: No such file or directory: ''
53 [255]
54
49 55 #if no-outer-repo
50 56
51 57 No repo:
52 58
53 59 $ hg cat
54 60 abort: no repository found in '$TESTTMP' (.hg not found)!
55 61 [255]
56 62
57 63 #endif
General Comments 0
You need to be logged in to leave comments. Login now