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