##// END OF EJS Templates
Disallow short earlygetop option combined with other short options...
Thomas Arendsen Hein -
r4732:b0520e39 default
parent child Browse files
Show More
@@ -1,1279 +1,1273 b''
1 1 # cmdutil.py - help for command processing in 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
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from node import *
9 9 from i18n import _
10 10 import os, sys, atexit, signal, pdb, traceback, socket, errno, shlex
11 11 import mdiff, bdiff, util, templater, patch, commands, hg, lock, time
12 12 import fancyopts, revlog, version, extensions, hook
13 13
14 14 revrangesep = ':'
15 15
16 16 class UnknownCommand(Exception):
17 17 """Exception raised if command is not in the command table."""
18 18 class AmbiguousCommand(Exception):
19 19 """Exception raised if command shortcut matches more than one command."""
20 20 class ParseError(Exception):
21 21 """Exception raised on errors in parsing the command line."""
22 22
23 23 def runcatch(ui, args, argv0=None):
24 24 def catchterm(*args):
25 25 raise util.SignalInterrupt
26 26
27 27 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
28 28 num = getattr(signal, name, None)
29 29 if num: signal.signal(num, catchterm)
30 30
31 31 try:
32 32 try:
33 33 # enter the debugger before command execution
34 34 if '--debugger' in args:
35 35 pdb.set_trace()
36 36 try:
37 37 return dispatch(ui, args, argv0=argv0)
38 38 finally:
39 39 ui.flush()
40 40 except:
41 41 # enter the debugger when we hit an exception
42 42 if '--debugger' in args:
43 43 pdb.post_mortem(sys.exc_info()[2])
44 44 ui.print_exc()
45 45 raise
46 46
47 47 except ParseError, inst:
48 48 if inst.args[0]:
49 49 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
50 50 commands.help_(ui, inst.args[0])
51 51 else:
52 52 ui.warn(_("hg: %s\n") % inst.args[1])
53 53 commands.help_(ui, 'shortlist')
54 54 except AmbiguousCommand, inst:
55 55 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
56 56 (inst.args[0], " ".join(inst.args[1])))
57 57 except UnknownCommand, inst:
58 58 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
59 59 commands.help_(ui, 'shortlist')
60 60 except hg.RepoError, inst:
61 61 ui.warn(_("abort: %s!\n") % inst)
62 62 except lock.LockHeld, inst:
63 63 if inst.errno == errno.ETIMEDOUT:
64 64 reason = _('timed out waiting for lock held by %s') % inst.locker
65 65 else:
66 66 reason = _('lock held by %s') % inst.locker
67 67 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
68 68 except lock.LockUnavailable, inst:
69 69 ui.warn(_("abort: could not lock %s: %s\n") %
70 70 (inst.desc or inst.filename, inst.strerror))
71 71 except revlog.RevlogError, inst:
72 72 ui.warn(_("abort: %s!\n") % inst)
73 73 except util.SignalInterrupt:
74 74 ui.warn(_("killed!\n"))
75 75 except KeyboardInterrupt:
76 76 try:
77 77 ui.warn(_("interrupted!\n"))
78 78 except IOError, inst:
79 79 if inst.errno == errno.EPIPE:
80 80 if ui.debugflag:
81 81 ui.warn(_("\nbroken pipe\n"))
82 82 else:
83 83 raise
84 84 except socket.error, inst:
85 85 ui.warn(_("abort: %s\n") % inst[1])
86 86 except IOError, inst:
87 87 if hasattr(inst, "code"):
88 88 ui.warn(_("abort: %s\n") % inst)
89 89 elif hasattr(inst, "reason"):
90 90 try: # usually it is in the form (errno, strerror)
91 91 reason = inst.reason.args[1]
92 92 except: # it might be anything, for example a string
93 93 reason = inst.reason
94 94 ui.warn(_("abort: error: %s\n") % reason)
95 95 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
96 96 if ui.debugflag:
97 97 ui.warn(_("broken pipe\n"))
98 98 elif getattr(inst, "strerror", None):
99 99 if getattr(inst, "filename", None):
100 100 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
101 101 else:
102 102 ui.warn(_("abort: %s\n") % inst.strerror)
103 103 else:
104 104 raise
105 105 except OSError, inst:
106 106 if getattr(inst, "filename", None):
107 107 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
108 108 else:
109 109 ui.warn(_("abort: %s\n") % inst.strerror)
110 110 except util.UnexpectedOutput, inst:
111 111 ui.warn(_("abort: %s") % inst[0])
112 112 if not isinstance(inst[1], basestring):
113 113 ui.warn(" %r\n" % (inst[1],))
114 114 elif not inst[1]:
115 115 ui.warn(_(" empty string\n"))
116 116 else:
117 117 ui.warn("\n%r\n" % util.ellipsis(inst[1]))
118 118 except ImportError, inst:
119 119 m = str(inst).split()[-1]
120 120 ui.warn(_("abort: could not import module %s!\n" % m))
121 121 if m in "mpatch bdiff".split():
122 122 ui.warn(_("(did you forget to compile extensions?)\n"))
123 123 elif m in "zlib".split():
124 124 ui.warn(_("(is your Python install correct?)\n"))
125 125
126 126 except util.Abort, inst:
127 127 ui.warn(_("abort: %s\n") % inst)
128 128 except SystemExit, inst:
129 129 # Commands shouldn't sys.exit directly, but give a return code.
130 130 # Just in case catch this and and pass exit code to caller.
131 131 return inst.code
132 132 except:
133 133 ui.warn(_("** unknown exception encountered, details follow\n"))
134 134 ui.warn(_("** report bug details to "
135 135 "http://www.selenic.com/mercurial/bts\n"))
136 136 ui.warn(_("** or mercurial@selenic.com\n"))
137 137 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
138 138 % version.get_version())
139 139 raise
140 140
141 141 return -1
142 142
143 143 def findpossible(ui, cmd):
144 144 """
145 145 Return cmd -> (aliases, command table entry)
146 146 for each matching command.
147 147 Return debug commands (or their aliases) only if no normal command matches.
148 148 """
149 149 choice = {}
150 150 debugchoice = {}
151 151 for e in commands.table.keys():
152 152 aliases = e.lstrip("^").split("|")
153 153 found = None
154 154 if cmd in aliases:
155 155 found = cmd
156 156 elif not ui.config("ui", "strict"):
157 157 for a in aliases:
158 158 if a.startswith(cmd):
159 159 found = a
160 160 break
161 161 if found is not None:
162 162 if aliases[0].startswith("debug") or found.startswith("debug"):
163 163 debugchoice[found] = (aliases, commands.table[e])
164 164 else:
165 165 choice[found] = (aliases, commands.table[e])
166 166
167 167 if not choice and debugchoice:
168 168 choice = debugchoice
169 169
170 170 return choice
171 171
172 172 def findcmd(ui, cmd):
173 173 """Return (aliases, command table entry) for command string."""
174 174 choice = findpossible(ui, cmd)
175 175
176 176 if choice.has_key(cmd):
177 177 return choice[cmd]
178 178
179 179 if len(choice) > 1:
180 180 clist = choice.keys()
181 181 clist.sort()
182 182 raise AmbiguousCommand(cmd, clist)
183 183
184 184 if choice:
185 185 return choice.values()[0]
186 186
187 187 raise UnknownCommand(cmd)
188 188
189 189 def findrepo():
190 190 p = os.getcwd()
191 191 while not os.path.isdir(os.path.join(p, ".hg")):
192 192 oldp, p = p, os.path.dirname(p)
193 193 if p == oldp:
194 194 return None
195 195
196 196 return p
197 197
198 198 def parse(ui, args):
199 199 options = {}
200 200 cmdoptions = {}
201 201
202 202 try:
203 203 args = fancyopts.fancyopts(args, commands.globalopts, options)
204 204 except fancyopts.getopt.GetoptError, inst:
205 205 raise ParseError(None, inst)
206 206
207 207 if args:
208 208 cmd, args = args[0], args[1:]
209 209 aliases, i = findcmd(ui, cmd)
210 210 cmd = aliases[0]
211 211 defaults = ui.config("defaults", cmd)
212 212 if defaults:
213 213 args = shlex.split(defaults) + args
214 214 c = list(i[1])
215 215 else:
216 216 cmd = None
217 217 c = []
218 218
219 219 # combine global options into local
220 220 for o in commands.globalopts:
221 221 c.append((o[0], o[1], options[o[1]], o[3]))
222 222
223 223 try:
224 224 args = fancyopts.fancyopts(args, c, cmdoptions)
225 225 except fancyopts.getopt.GetoptError, inst:
226 226 raise ParseError(cmd, inst)
227 227
228 228 # separate global options back out
229 229 for o in commands.globalopts:
230 230 n = o[1]
231 231 options[n] = cmdoptions[n]
232 232 del cmdoptions[n]
233 233
234 234 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
235 235
236 236 def parseconfig(config):
237 237 """parse the --config options from the command line"""
238 238 parsed = []
239 239 for cfg in config:
240 240 try:
241 241 name, value = cfg.split('=', 1)
242 242 section, name = name.split('.', 1)
243 243 if not section or not name:
244 244 raise IndexError
245 245 parsed.append((section, name, value))
246 246 except (IndexError, ValueError):
247 247 raise util.Abort(_('malformed --config option: %s') % cfg)
248 248 return parsed
249 249
250 250 def earlygetopt(aliases, args):
251 251 """Return list of values for a option (with aliases) in given order
252 252
253 253 Short option aliases have to occur before long aliases, e.g.:
254 254 earlygetopt(["-R", "--repository", "--repo"], args)
255 255 (this is not checked!)
256 256 """
257 257 try:
258 258 argcount = args.index("--")
259 259 except ValueError:
260 260 argcount = len(args)
261 261 values = []
262 262 pos = 0
263 263 while pos < argcount:
264 264 valuepos = argcount
265 265 for opt in aliases:
266 # short option can have no spaces, e.g. hg log -qRfoo:
266 # short option can have no following space, e.g. hg log -Rfoo:
267 267 if len(opt) == 2:
268 268 i = argcount
269 269 while i > 0:
270 270 i -= 1
271 271 arg = args[i]
272 if len(arg) > 2 and arg[0] == '-' and arg[1] != '-':
273 optpos = arg.find(opt[1])
274 # split Rfoo -> R foo
275 if 0 < optpos < len(arg)-1:
276 args[i:i+1] = [arg[:optpos+1], arg[optpos+1:]]
277 argcount += 1
278 # split -qR -> -q -R
279 if optpos > 1:
280 args[i:i+1] = [arg[:optpos], opt]
281 argcount += 1
272 if len(arg) > 2 and arg.startswith(opt):
273 # split -Rfoo -> -R foo
274 args[i:i+1] = [opt, arg[2:]]
275 argcount += 1
282 276 # find next occurance of current alias
283 277 try:
284 278 candidate = args.index(opt, pos, argcount) + 1
285 279 # ignore and let getopt report an error if there is no value
286 280 if candidate < valuepos:
287 281 valuepos = candidate
288 282 except ValueError:
289 283 pass
290 284 if valuepos < argcount:
291 285 values.append(args[valuepos])
292 286 pos = valuepos
293 287 return values
294 288
295 289 def dispatch(ui, args, argv0=None):
296 290 # remember how to call 'hg' before changing the working dir
297 291 util.set_hgexecutable(argv0)
298 292
299 293 # read --config before doing anything else
300 294 # (e.g. to change trust settings for reading .hg/hgrc)
301 295 config = earlygetopt(['--config'], args)
302 296 if config:
303 297 ui.updateopts(config=parseconfig(config))
304 298
305 299 # check for cwd
306 300 cwd = earlygetopt(['--cwd'], args)
307 301 if cwd:
308 302 os.chdir(cwd[-1])
309 303
310 304 # read the local repository .hgrc into a local ui object
311 305 path = findrepo() or ""
312 306 if not path:
313 307 lui = ui
314 308 if path:
315 309 try:
316 310 lui = commands.ui.ui(parentui=ui)
317 311 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
318 312 except IOError:
319 313 pass
320 314
321 315 # now we can expand paths, even ones in .hg/hgrc
322 316 rpath = earlygetopt(["-R", "--repository", "--repo"], args)
323 317 if rpath:
324 318 path = lui.expandpath(rpath[-1])
325 319 lui = commands.ui.ui(parentui=ui)
326 320 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
327 321
328 322 extensions.loadall(lui)
329 323 # check for fallback encoding
330 324 fallback = lui.config('ui', 'fallbackencoding')
331 325 if fallback:
332 326 util._fallbackencoding = fallback
333 327
334 328 fullargs = args
335 329 cmd, func, args, options, cmdoptions = parse(ui, args)
336 330
337 331 if options["encoding"]:
338 332 util._encoding = options["encoding"]
339 333 if options["encodingmode"]:
340 334 util._encodingmode = options["encodingmode"]
341 335 if options["time"]:
342 336 def get_times():
343 337 t = os.times()
344 338 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
345 339 t = (t[0], t[1], t[2], t[3], time.clock())
346 340 return t
347 341 s = get_times()
348 342 def print_time():
349 343 t = get_times()
350 344 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
351 345 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
352 346 atexit.register(print_time)
353 347
354 348 ui.updateopts(options["verbose"], options["debug"], options["quiet"],
355 349 not options["noninteractive"], options["traceback"])
356 350
357 351 if options['help']:
358 352 return commands.help_(ui, cmd, options['version'])
359 353 elif options['version']:
360 354 return commands.version_(ui)
361 355 elif not cmd:
362 356 return commands.help_(ui, 'shortlist')
363 357
364 358 repo = None
365 359 if cmd not in commands.norepo.split():
366 360 try:
367 361 repo = hg.repository(ui, path=path)
368 362 ui = repo.ui
369 363 if not repo.local():
370 364 raise util.Abort(_("repository '%s' is not local") % path)
371 365 except hg.RepoError:
372 366 if cmd not in commands.optionalrepo.split():
373 367 if not path:
374 368 raise hg.RepoError(_("There is no Mercurial repository here"
375 369 " (.hg not found)"))
376 370 raise
377 371 d = lambda: func(ui, repo, *args, **cmdoptions)
378 372 else:
379 373 d = lambda: func(ui, *args, **cmdoptions)
380 374
381 375 # run pre-hook, and abort if it fails
382 376 ret = hook.hook(ui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs))
383 377 if ret:
384 378 return ret
385 379 ret = runcommand(ui, options, cmd, d)
386 380 # run post-hook, passing command result
387 381 hook.hook(ui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
388 382 result = ret)
389 383 return ret
390 384
391 385 def runcommand(ui, options, cmd, cmdfunc):
392 386 def checkargs():
393 387 try:
394 388 return cmdfunc()
395 389 except TypeError, inst:
396 390 # was this an argument error?
397 391 tb = traceback.extract_tb(sys.exc_info()[2])
398 392 if len(tb) != 2: # no
399 393 raise
400 394 raise ParseError(cmd, _("invalid arguments"))
401 395
402 396 if options['profile']:
403 397 import hotshot, hotshot.stats
404 398 prof = hotshot.Profile("hg.prof")
405 399 try:
406 400 try:
407 401 return prof.runcall(checkargs)
408 402 except:
409 403 try:
410 404 ui.warn(_('exception raised - generating '
411 405 'profile anyway\n'))
412 406 except:
413 407 pass
414 408 raise
415 409 finally:
416 410 prof.close()
417 411 stats = hotshot.stats.load("hg.prof")
418 412 stats.strip_dirs()
419 413 stats.sort_stats('time', 'calls')
420 414 stats.print_stats(40)
421 415 elif options['lsprof']:
422 416 try:
423 417 from mercurial import lsprof
424 418 except ImportError:
425 419 raise util.Abort(_(
426 420 'lsprof not available - install from '
427 421 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
428 422 p = lsprof.Profiler()
429 423 p.enable(subcalls=True)
430 424 try:
431 425 return checkargs()
432 426 finally:
433 427 p.disable()
434 428 stats = lsprof.Stats(p.getstats())
435 429 stats.sort()
436 430 stats.pprint(top=10, file=sys.stderr, climit=5)
437 431 else:
438 432 return checkargs()
439 433
440 434 def bail_if_changed(repo):
441 435 modified, added, removed, deleted = repo.status()[:4]
442 436 if modified or added or removed or deleted:
443 437 raise util.Abort(_("outstanding uncommitted changes"))
444 438
445 439 def logmessage(opts):
446 440 """ get the log message according to -m and -l option """
447 441 message = opts['message']
448 442 logfile = opts['logfile']
449 443
450 444 if message and logfile:
451 445 raise util.Abort(_('options --message and --logfile are mutually '
452 446 'exclusive'))
453 447 if not message and logfile:
454 448 try:
455 449 if logfile == '-':
456 450 message = sys.stdin.read()
457 451 else:
458 452 message = open(logfile).read()
459 453 except IOError, inst:
460 454 raise util.Abort(_("can't read commit message '%s': %s") %
461 455 (logfile, inst.strerror))
462 456 return message
463 457
464 458 def setremoteconfig(ui, opts):
465 459 "copy remote options to ui tree"
466 460 if opts.get('ssh'):
467 461 ui.setconfig("ui", "ssh", opts['ssh'])
468 462 if opts.get('remotecmd'):
469 463 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
470 464
471 465 def parseurl(url, revs):
472 466 '''parse url#branch, returning url, branch + revs'''
473 467
474 468 if '#' not in url:
475 469 return url, (revs or None)
476 470
477 471 url, rev = url.split('#', 1)
478 472 return url, revs + [rev]
479 473
480 474 def revpair(repo, revs):
481 475 '''return pair of nodes, given list of revisions. second item can
482 476 be None, meaning use working dir.'''
483 477
484 478 def revfix(repo, val, defval):
485 479 if not val and val != 0 and defval is not None:
486 480 val = defval
487 481 return repo.lookup(val)
488 482
489 483 if not revs:
490 484 return repo.dirstate.parents()[0], None
491 485 end = None
492 486 if len(revs) == 1:
493 487 if revrangesep in revs[0]:
494 488 start, end = revs[0].split(revrangesep, 1)
495 489 start = revfix(repo, start, 0)
496 490 end = revfix(repo, end, repo.changelog.count() - 1)
497 491 else:
498 492 start = revfix(repo, revs[0], None)
499 493 elif len(revs) == 2:
500 494 if revrangesep in revs[0] or revrangesep in revs[1]:
501 495 raise util.Abort(_('too many revisions specified'))
502 496 start = revfix(repo, revs[0], None)
503 497 end = revfix(repo, revs[1], None)
504 498 else:
505 499 raise util.Abort(_('too many revisions specified'))
506 500 return start, end
507 501
508 502 def revrange(repo, revs):
509 503 """Yield revision as strings from a list of revision specifications."""
510 504
511 505 def revfix(repo, val, defval):
512 506 if not val and val != 0 and defval is not None:
513 507 return defval
514 508 return repo.changelog.rev(repo.lookup(val))
515 509
516 510 seen, l = {}, []
517 511 for spec in revs:
518 512 if revrangesep in spec:
519 513 start, end = spec.split(revrangesep, 1)
520 514 start = revfix(repo, start, 0)
521 515 end = revfix(repo, end, repo.changelog.count() - 1)
522 516 step = start > end and -1 or 1
523 517 for rev in xrange(start, end+step, step):
524 518 if rev in seen:
525 519 continue
526 520 seen[rev] = 1
527 521 l.append(rev)
528 522 else:
529 523 rev = revfix(repo, spec, None)
530 524 if rev in seen:
531 525 continue
532 526 seen[rev] = 1
533 527 l.append(rev)
534 528
535 529 return l
536 530
537 531 def make_filename(repo, pat, node,
538 532 total=None, seqno=None, revwidth=None, pathname=None):
539 533 node_expander = {
540 534 'H': lambda: hex(node),
541 535 'R': lambda: str(repo.changelog.rev(node)),
542 536 'h': lambda: short(node),
543 537 }
544 538 expander = {
545 539 '%': lambda: '%',
546 540 'b': lambda: os.path.basename(repo.root),
547 541 }
548 542
549 543 try:
550 544 if node:
551 545 expander.update(node_expander)
552 546 if node and revwidth is not None:
553 547 expander['r'] = (lambda:
554 548 str(repo.changelog.rev(node)).zfill(revwidth))
555 549 if total is not None:
556 550 expander['N'] = lambda: str(total)
557 551 if seqno is not None:
558 552 expander['n'] = lambda: str(seqno)
559 553 if total is not None and seqno is not None:
560 554 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
561 555 if pathname is not None:
562 556 expander['s'] = lambda: os.path.basename(pathname)
563 557 expander['d'] = lambda: os.path.dirname(pathname) or '.'
564 558 expander['p'] = lambda: pathname
565 559
566 560 newname = []
567 561 patlen = len(pat)
568 562 i = 0
569 563 while i < patlen:
570 564 c = pat[i]
571 565 if c == '%':
572 566 i += 1
573 567 c = pat[i]
574 568 c = expander[c]()
575 569 newname.append(c)
576 570 i += 1
577 571 return ''.join(newname)
578 572 except KeyError, inst:
579 573 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
580 574 inst.args[0])
581 575
582 576 def make_file(repo, pat, node=None,
583 577 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
584 578 if not pat or pat == '-':
585 579 return 'w' in mode and sys.stdout or sys.stdin
586 580 if hasattr(pat, 'write') and 'w' in mode:
587 581 return pat
588 582 if hasattr(pat, 'read') and 'r' in mode:
589 583 return pat
590 584 return open(make_filename(repo, pat, node, total, seqno, revwidth,
591 585 pathname),
592 586 mode)
593 587
594 588 def matchpats(repo, pats=[], opts={}, globbed=False, default=None):
595 589 cwd = repo.getcwd()
596 590 return util.cmdmatcher(repo.root, cwd, pats or [], opts.get('include'),
597 591 opts.get('exclude'), globbed=globbed,
598 592 default=default)
599 593
600 594 def walk(repo, pats=[], opts={}, node=None, badmatch=None, globbed=False,
601 595 default=None):
602 596 files, matchfn, anypats = matchpats(repo, pats, opts, globbed=globbed,
603 597 default=default)
604 598 exact = dict.fromkeys(files)
605 599 cwd = repo.getcwd()
606 600 for src, fn in repo.walk(node=node, files=files, match=matchfn,
607 601 badmatch=badmatch):
608 602 yield src, fn, repo.pathto(fn, cwd), fn in exact
609 603
610 604 def findrenames(repo, added=None, removed=None, threshold=0.5):
611 605 '''find renamed files -- yields (before, after, score) tuples'''
612 606 if added is None or removed is None:
613 607 added, removed = repo.status()[1:3]
614 608 ctx = repo.changectx()
615 609 for a in added:
616 610 aa = repo.wread(a)
617 611 bestname, bestscore = None, threshold
618 612 for r in removed:
619 613 rr = ctx.filectx(r).data()
620 614
621 615 # bdiff.blocks() returns blocks of matching lines
622 616 # count the number of bytes in each
623 617 equal = 0
624 618 alines = mdiff.splitnewlines(aa)
625 619 matches = bdiff.blocks(aa, rr)
626 620 for x1,x2,y1,y2 in matches:
627 621 for line in alines[x1:x2]:
628 622 equal += len(line)
629 623
630 624 lengths = len(aa) + len(rr)
631 625 if lengths:
632 626 myscore = equal*2.0 / lengths
633 627 if myscore >= bestscore:
634 628 bestname, bestscore = r, myscore
635 629 if bestname:
636 630 yield bestname, a, bestscore
637 631
638 632 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None,
639 633 similarity=None):
640 634 if dry_run is None:
641 635 dry_run = opts.get('dry_run')
642 636 if similarity is None:
643 637 similarity = float(opts.get('similarity') or 0)
644 638 add, remove = [], []
645 639 mapping = {}
646 640 for src, abs, rel, exact in walk(repo, pats, opts):
647 641 target = repo.wjoin(abs)
648 642 if src == 'f' and repo.dirstate.state(abs) == '?':
649 643 add.append(abs)
650 644 mapping[abs] = rel, exact
651 645 if repo.ui.verbose or not exact:
652 646 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
653 647 if repo.dirstate.state(abs) != 'r' and not util.lexists(target):
654 648 remove.append(abs)
655 649 mapping[abs] = rel, exact
656 650 if repo.ui.verbose or not exact:
657 651 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
658 652 if not dry_run:
659 653 repo.add(add, wlock=wlock)
660 654 repo.remove(remove, wlock=wlock)
661 655 if similarity > 0:
662 656 for old, new, score in findrenames(repo, add, remove, similarity):
663 657 oldrel, oldexact = mapping[old]
664 658 newrel, newexact = mapping[new]
665 659 if repo.ui.verbose or not oldexact or not newexact:
666 660 repo.ui.status(_('recording removal of %s as rename to %s '
667 661 '(%d%% similar)\n') %
668 662 (oldrel, newrel, score * 100))
669 663 if not dry_run:
670 664 repo.copy(old, new, wlock=wlock)
671 665
672 666 def service(opts, parentfn=None, initfn=None, runfn=None):
673 667 '''Run a command as a service.'''
674 668
675 669 if opts['daemon'] and not opts['daemon_pipefds']:
676 670 rfd, wfd = os.pipe()
677 671 args = sys.argv[:]
678 672 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
679 673 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
680 674 args[0], args)
681 675 os.close(wfd)
682 676 os.read(rfd, 1)
683 677 if parentfn:
684 678 return parentfn(pid)
685 679 else:
686 680 os._exit(0)
687 681
688 682 if initfn:
689 683 initfn()
690 684
691 685 if opts['pid_file']:
692 686 fp = open(opts['pid_file'], 'w')
693 687 fp.write(str(os.getpid()) + '\n')
694 688 fp.close()
695 689
696 690 if opts['daemon_pipefds']:
697 691 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
698 692 os.close(rfd)
699 693 try:
700 694 os.setsid()
701 695 except AttributeError:
702 696 pass
703 697 os.write(wfd, 'y')
704 698 os.close(wfd)
705 699 sys.stdout.flush()
706 700 sys.stderr.flush()
707 701 fd = os.open(util.nulldev, os.O_RDWR)
708 702 if fd != 0: os.dup2(fd, 0)
709 703 if fd != 1: os.dup2(fd, 1)
710 704 if fd != 2: os.dup2(fd, 2)
711 705 if fd not in (0, 1, 2): os.close(fd)
712 706
713 707 if runfn:
714 708 return runfn()
715 709
716 710 class changeset_printer(object):
717 711 '''show changeset information when templating not requested.'''
718 712
719 713 def __init__(self, ui, repo, patch, buffered):
720 714 self.ui = ui
721 715 self.repo = repo
722 716 self.buffered = buffered
723 717 self.patch = patch
724 718 self.header = {}
725 719 self.hunk = {}
726 720 self.lastheader = None
727 721
728 722 def flush(self, rev):
729 723 if rev in self.header:
730 724 h = self.header[rev]
731 725 if h != self.lastheader:
732 726 self.lastheader = h
733 727 self.ui.write(h)
734 728 del self.header[rev]
735 729 if rev in self.hunk:
736 730 self.ui.write(self.hunk[rev])
737 731 del self.hunk[rev]
738 732 return 1
739 733 return 0
740 734
741 735 def show(self, rev=0, changenode=None, copies=(), **props):
742 736 if self.buffered:
743 737 self.ui.pushbuffer()
744 738 self._show(rev, changenode, copies, props)
745 739 self.hunk[rev] = self.ui.popbuffer()
746 740 else:
747 741 self._show(rev, changenode, copies, props)
748 742
749 743 def _show(self, rev, changenode, copies, props):
750 744 '''show a single changeset or file revision'''
751 745 log = self.repo.changelog
752 746 if changenode is None:
753 747 changenode = log.node(rev)
754 748 elif not rev:
755 749 rev = log.rev(changenode)
756 750
757 751 if self.ui.quiet:
758 752 self.ui.write("%d:%s\n" % (rev, short(changenode)))
759 753 return
760 754
761 755 changes = log.read(changenode)
762 756 date = util.datestr(changes[2])
763 757 extra = changes[5]
764 758 branch = extra.get("branch")
765 759
766 760 hexfunc = self.ui.debugflag and hex or short
767 761
768 762 parents = log.parentrevs(rev)
769 763 if not self.ui.debugflag:
770 764 if parents[1] == nullrev:
771 765 if parents[0] >= rev - 1:
772 766 parents = []
773 767 else:
774 768 parents = [parents[0]]
775 769 parents = [(p, hexfunc(log.node(p))) for p in parents]
776 770
777 771 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
778 772
779 773 # don't show the default branch name
780 774 if branch != 'default':
781 775 branch = util.tolocal(branch)
782 776 self.ui.write(_("branch: %s\n") % branch)
783 777 for tag in self.repo.nodetags(changenode):
784 778 self.ui.write(_("tag: %s\n") % tag)
785 779 for parent in parents:
786 780 self.ui.write(_("parent: %d:%s\n") % parent)
787 781
788 782 if self.ui.debugflag:
789 783 self.ui.write(_("manifest: %d:%s\n") %
790 784 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
791 785 self.ui.write(_("user: %s\n") % changes[1])
792 786 self.ui.write(_("date: %s\n") % date)
793 787
794 788 if self.ui.debugflag:
795 789 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
796 790 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
797 791 files):
798 792 if value:
799 793 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
800 794 elif changes[3] and self.ui.verbose:
801 795 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
802 796 if copies and self.ui.verbose:
803 797 copies = ['%s (%s)' % c for c in copies]
804 798 self.ui.write(_("copies: %s\n") % ' '.join(copies))
805 799
806 800 if extra and self.ui.debugflag:
807 801 extraitems = extra.items()
808 802 extraitems.sort()
809 803 for key, value in extraitems:
810 804 self.ui.write(_("extra: %s=%s\n")
811 805 % (key, value.encode('string_escape')))
812 806
813 807 description = changes[4].strip()
814 808 if description:
815 809 if self.ui.verbose:
816 810 self.ui.write(_("description:\n"))
817 811 self.ui.write(description)
818 812 self.ui.write("\n\n")
819 813 else:
820 814 self.ui.write(_("summary: %s\n") %
821 815 description.splitlines()[0])
822 816 self.ui.write("\n")
823 817
824 818 self.showpatch(changenode)
825 819
826 820 def showpatch(self, node):
827 821 if self.patch:
828 822 prev = self.repo.changelog.parents(node)[0]
829 823 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui,
830 824 opts=patch.diffopts(self.ui))
831 825 self.ui.write("\n")
832 826
833 827 class changeset_templater(changeset_printer):
834 828 '''format changeset information.'''
835 829
836 830 def __init__(self, ui, repo, patch, mapfile, buffered):
837 831 changeset_printer.__init__(self, ui, repo, patch, buffered)
838 832 filters = templater.common_filters.copy()
839 833 filters['formatnode'] = (ui.debugflag and (lambda x: x)
840 834 or (lambda x: x[:12]))
841 835 self.t = templater.templater(mapfile, filters,
842 836 cache={
843 837 'parent': '{rev}:{node|formatnode} ',
844 838 'manifest': '{rev}:{node|formatnode}',
845 839 'filecopy': '{name} ({source})'})
846 840
847 841 def use_template(self, t):
848 842 '''set template string to use'''
849 843 self.t.cache['changeset'] = t
850 844
851 845 def _show(self, rev, changenode, copies, props):
852 846 '''show a single changeset or file revision'''
853 847 log = self.repo.changelog
854 848 if changenode is None:
855 849 changenode = log.node(rev)
856 850 elif not rev:
857 851 rev = log.rev(changenode)
858 852
859 853 changes = log.read(changenode)
860 854
861 855 def showlist(name, values, plural=None, **args):
862 856 '''expand set of values.
863 857 name is name of key in template map.
864 858 values is list of strings or dicts.
865 859 plural is plural of name, if not simply name + 's'.
866 860
867 861 expansion works like this, given name 'foo'.
868 862
869 863 if values is empty, expand 'no_foos'.
870 864
871 865 if 'foo' not in template map, return values as a string,
872 866 joined by space.
873 867
874 868 expand 'start_foos'.
875 869
876 870 for each value, expand 'foo'. if 'last_foo' in template
877 871 map, expand it instead of 'foo' for last key.
878 872
879 873 expand 'end_foos'.
880 874 '''
881 875 if plural: names = plural
882 876 else: names = name + 's'
883 877 if not values:
884 878 noname = 'no_' + names
885 879 if noname in self.t:
886 880 yield self.t(noname, **args)
887 881 return
888 882 if name not in self.t:
889 883 if isinstance(values[0], str):
890 884 yield ' '.join(values)
891 885 else:
892 886 for v in values:
893 887 yield dict(v, **args)
894 888 return
895 889 startname = 'start_' + names
896 890 if startname in self.t:
897 891 yield self.t(startname, **args)
898 892 vargs = args.copy()
899 893 def one(v, tag=name):
900 894 try:
901 895 vargs.update(v)
902 896 except (AttributeError, ValueError):
903 897 try:
904 898 for a, b in v:
905 899 vargs[a] = b
906 900 except ValueError:
907 901 vargs[name] = v
908 902 return self.t(tag, **vargs)
909 903 lastname = 'last_' + name
910 904 if lastname in self.t:
911 905 last = values.pop()
912 906 else:
913 907 last = None
914 908 for v in values:
915 909 yield one(v)
916 910 if last is not None:
917 911 yield one(last, tag=lastname)
918 912 endname = 'end_' + names
919 913 if endname in self.t:
920 914 yield self.t(endname, **args)
921 915
922 916 def showbranches(**args):
923 917 branch = changes[5].get("branch")
924 918 if branch != 'default':
925 919 branch = util.tolocal(branch)
926 920 return showlist('branch', [branch], plural='branches', **args)
927 921
928 922 def showparents(**args):
929 923 parents = [[('rev', log.rev(p)), ('node', hex(p))]
930 924 for p in log.parents(changenode)
931 925 if self.ui.debugflag or p != nullid]
932 926 if (not self.ui.debugflag and len(parents) == 1 and
933 927 parents[0][0][1] == rev - 1):
934 928 return
935 929 return showlist('parent', parents, **args)
936 930
937 931 def showtags(**args):
938 932 return showlist('tag', self.repo.nodetags(changenode), **args)
939 933
940 934 def showextras(**args):
941 935 extras = changes[5].items()
942 936 extras.sort()
943 937 for key, value in extras:
944 938 args = args.copy()
945 939 args.update(dict(key=key, value=value))
946 940 yield self.t('extra', **args)
947 941
948 942 def showcopies(**args):
949 943 c = [{'name': x[0], 'source': x[1]} for x in copies]
950 944 return showlist('file_copy', c, plural='file_copies', **args)
951 945
952 946 if self.ui.debugflag:
953 947 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
954 948 def showfiles(**args):
955 949 return showlist('file', files[0], **args)
956 950 def showadds(**args):
957 951 return showlist('file_add', files[1], **args)
958 952 def showdels(**args):
959 953 return showlist('file_del', files[2], **args)
960 954 def showmanifest(**args):
961 955 args = args.copy()
962 956 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
963 957 node=hex(changes[0])))
964 958 return self.t('manifest', **args)
965 959 else:
966 960 def showfiles(**args):
967 961 return showlist('file', changes[3], **args)
968 962 showadds = ''
969 963 showdels = ''
970 964 showmanifest = ''
971 965
972 966 defprops = {
973 967 'author': changes[1],
974 968 'branches': showbranches,
975 969 'date': changes[2],
976 970 'desc': changes[4],
977 971 'file_adds': showadds,
978 972 'file_dels': showdels,
979 973 'files': showfiles,
980 974 'file_copies': showcopies,
981 975 'manifest': showmanifest,
982 976 'node': hex(changenode),
983 977 'parents': showparents,
984 978 'rev': rev,
985 979 'tags': showtags,
986 980 'extras': showextras,
987 981 }
988 982 props = props.copy()
989 983 props.update(defprops)
990 984
991 985 try:
992 986 if self.ui.debugflag and 'header_debug' in self.t:
993 987 key = 'header_debug'
994 988 elif self.ui.quiet and 'header_quiet' in self.t:
995 989 key = 'header_quiet'
996 990 elif self.ui.verbose and 'header_verbose' in self.t:
997 991 key = 'header_verbose'
998 992 elif 'header' in self.t:
999 993 key = 'header'
1000 994 else:
1001 995 key = ''
1002 996 if key:
1003 997 h = templater.stringify(self.t(key, **props))
1004 998 if self.buffered:
1005 999 self.header[rev] = h
1006 1000 else:
1007 1001 self.ui.write(h)
1008 1002 if self.ui.debugflag and 'changeset_debug' in self.t:
1009 1003 key = 'changeset_debug'
1010 1004 elif self.ui.quiet and 'changeset_quiet' in self.t:
1011 1005 key = 'changeset_quiet'
1012 1006 elif self.ui.verbose and 'changeset_verbose' in self.t:
1013 1007 key = 'changeset_verbose'
1014 1008 else:
1015 1009 key = 'changeset'
1016 1010 self.ui.write(templater.stringify(self.t(key, **props)))
1017 1011 self.showpatch(changenode)
1018 1012 except KeyError, inst:
1019 1013 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
1020 1014 inst.args[0]))
1021 1015 except SyntaxError, inst:
1022 1016 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
1023 1017
1024 1018 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
1025 1019 """show one changeset using template or regular display.
1026 1020
1027 1021 Display format will be the first non-empty hit of:
1028 1022 1. option 'template'
1029 1023 2. option 'style'
1030 1024 3. [ui] setting 'logtemplate'
1031 1025 4. [ui] setting 'style'
1032 1026 If all of these values are either the unset or the empty string,
1033 1027 regular display via changeset_printer() is done.
1034 1028 """
1035 1029 # options
1036 1030 patch = False
1037 1031 if opts.get('patch'):
1038 1032 patch = matchfn or util.always
1039 1033
1040 1034 tmpl = opts.get('template')
1041 1035 mapfile = None
1042 1036 if tmpl:
1043 1037 tmpl = templater.parsestring(tmpl, quoted=False)
1044 1038 else:
1045 1039 mapfile = opts.get('style')
1046 1040 # ui settings
1047 1041 if not mapfile:
1048 1042 tmpl = ui.config('ui', 'logtemplate')
1049 1043 if tmpl:
1050 1044 tmpl = templater.parsestring(tmpl)
1051 1045 else:
1052 1046 mapfile = ui.config('ui', 'style')
1053 1047
1054 1048 if tmpl or mapfile:
1055 1049 if mapfile:
1056 1050 if not os.path.split(mapfile)[0]:
1057 1051 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1058 1052 or templater.templatepath(mapfile))
1059 1053 if mapname: mapfile = mapname
1060 1054 try:
1061 1055 t = changeset_templater(ui, repo, patch, mapfile, buffered)
1062 1056 except SyntaxError, inst:
1063 1057 raise util.Abort(inst.args[0])
1064 1058 if tmpl: t.use_template(tmpl)
1065 1059 return t
1066 1060 return changeset_printer(ui, repo, patch, buffered)
1067 1061
1068 1062 def finddate(ui, repo, date):
1069 1063 """Find the tipmost changeset that matches the given date spec"""
1070 1064 df = util.matchdate(date + " to " + date)
1071 1065 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1072 1066 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
1073 1067 results = {}
1074 1068 for st, rev, fns in changeiter:
1075 1069 if st == 'add':
1076 1070 d = get(rev)[2]
1077 1071 if df(d[0]):
1078 1072 results[rev] = d
1079 1073 elif st == 'iter':
1080 1074 if rev in results:
1081 1075 ui.status("Found revision %s from %s\n" %
1082 1076 (rev, util.datestr(results[rev])))
1083 1077 return str(rev)
1084 1078
1085 1079 raise util.Abort(_("revision matching date not found"))
1086 1080
1087 1081 def walkchangerevs(ui, repo, pats, change, opts):
1088 1082 '''Iterate over files and the revs they changed in.
1089 1083
1090 1084 Callers most commonly need to iterate backwards over the history
1091 1085 it is interested in. Doing so has awful (quadratic-looking)
1092 1086 performance, so we use iterators in a "windowed" way.
1093 1087
1094 1088 We walk a window of revisions in the desired order. Within the
1095 1089 window, we first walk forwards to gather data, then in the desired
1096 1090 order (usually backwards) to display it.
1097 1091
1098 1092 This function returns an (iterator, matchfn) tuple. The iterator
1099 1093 yields 3-tuples. They will be of one of the following forms:
1100 1094
1101 1095 "window", incrementing, lastrev: stepping through a window,
1102 1096 positive if walking forwards through revs, last rev in the
1103 1097 sequence iterated over - use to reset state for the current window
1104 1098
1105 1099 "add", rev, fns: out-of-order traversal of the given file names
1106 1100 fns, which changed during revision rev - use to gather data for
1107 1101 possible display
1108 1102
1109 1103 "iter", rev, None: in-order traversal of the revs earlier iterated
1110 1104 over with "add" - use to display data'''
1111 1105
1112 1106 def increasing_windows(start, end, windowsize=8, sizelimit=512):
1113 1107 if start < end:
1114 1108 while start < end:
1115 1109 yield start, min(windowsize, end-start)
1116 1110 start += windowsize
1117 1111 if windowsize < sizelimit:
1118 1112 windowsize *= 2
1119 1113 else:
1120 1114 while start > end:
1121 1115 yield start, min(windowsize, start-end-1)
1122 1116 start -= windowsize
1123 1117 if windowsize < sizelimit:
1124 1118 windowsize *= 2
1125 1119
1126 1120 files, matchfn, anypats = matchpats(repo, pats, opts)
1127 1121 follow = opts.get('follow') or opts.get('follow_first')
1128 1122
1129 1123 if repo.changelog.count() == 0:
1130 1124 return [], matchfn
1131 1125
1132 1126 if follow:
1133 1127 defrange = '%s:0' % repo.changectx().rev()
1134 1128 else:
1135 1129 defrange = 'tip:0'
1136 1130 revs = revrange(repo, opts['rev'] or [defrange])
1137 1131 wanted = {}
1138 1132 slowpath = anypats or opts.get('removed')
1139 1133 fncache = {}
1140 1134
1141 1135 if not slowpath and not files:
1142 1136 # No files, no patterns. Display all revs.
1143 1137 wanted = dict.fromkeys(revs)
1144 1138 copies = []
1145 1139 if not slowpath:
1146 1140 # Only files, no patterns. Check the history of each file.
1147 1141 def filerevgen(filelog, node):
1148 1142 cl_count = repo.changelog.count()
1149 1143 if node is None:
1150 1144 last = filelog.count() - 1
1151 1145 else:
1152 1146 last = filelog.rev(node)
1153 1147 for i, window in increasing_windows(last, nullrev):
1154 1148 revs = []
1155 1149 for j in xrange(i - window, i + 1):
1156 1150 n = filelog.node(j)
1157 1151 revs.append((filelog.linkrev(n),
1158 1152 follow and filelog.renamed(n)))
1159 1153 revs.reverse()
1160 1154 for rev in revs:
1161 1155 # only yield rev for which we have the changelog, it can
1162 1156 # happen while doing "hg log" during a pull or commit
1163 1157 if rev[0] < cl_count:
1164 1158 yield rev
1165 1159 def iterfiles():
1166 1160 for filename in files:
1167 1161 yield filename, None
1168 1162 for filename_node in copies:
1169 1163 yield filename_node
1170 1164 minrev, maxrev = min(revs), max(revs)
1171 1165 for file_, node in iterfiles():
1172 1166 filelog = repo.file(file_)
1173 1167 # A zero count may be a directory or deleted file, so
1174 1168 # try to find matching entries on the slow path.
1175 1169 if filelog.count() == 0:
1176 1170 slowpath = True
1177 1171 break
1178 1172 for rev, copied in filerevgen(filelog, node):
1179 1173 if rev <= maxrev:
1180 1174 if rev < minrev:
1181 1175 break
1182 1176 fncache.setdefault(rev, [])
1183 1177 fncache[rev].append(file_)
1184 1178 wanted[rev] = 1
1185 1179 if follow and copied:
1186 1180 copies.append(copied)
1187 1181 if slowpath:
1188 1182 if follow:
1189 1183 raise util.Abort(_('can only follow copies/renames for explicit '
1190 1184 'file names'))
1191 1185
1192 1186 # The slow path checks files modified in every changeset.
1193 1187 def changerevgen():
1194 1188 for i, window in increasing_windows(repo.changelog.count()-1,
1195 1189 nullrev):
1196 1190 for j in xrange(i - window, i + 1):
1197 1191 yield j, change(j)[3]
1198 1192
1199 1193 for rev, changefiles in changerevgen():
1200 1194 matches = filter(matchfn, changefiles)
1201 1195 if matches:
1202 1196 fncache[rev] = matches
1203 1197 wanted[rev] = 1
1204 1198
1205 1199 class followfilter:
1206 1200 def __init__(self, onlyfirst=False):
1207 1201 self.startrev = nullrev
1208 1202 self.roots = []
1209 1203 self.onlyfirst = onlyfirst
1210 1204
1211 1205 def match(self, rev):
1212 1206 def realparents(rev):
1213 1207 if self.onlyfirst:
1214 1208 return repo.changelog.parentrevs(rev)[0:1]
1215 1209 else:
1216 1210 return filter(lambda x: x != nullrev,
1217 1211 repo.changelog.parentrevs(rev))
1218 1212
1219 1213 if self.startrev == nullrev:
1220 1214 self.startrev = rev
1221 1215 return True
1222 1216
1223 1217 if rev > self.startrev:
1224 1218 # forward: all descendants
1225 1219 if not self.roots:
1226 1220 self.roots.append(self.startrev)
1227 1221 for parent in realparents(rev):
1228 1222 if parent in self.roots:
1229 1223 self.roots.append(rev)
1230 1224 return True
1231 1225 else:
1232 1226 # backwards: all parents
1233 1227 if not self.roots:
1234 1228 self.roots.extend(realparents(self.startrev))
1235 1229 if rev in self.roots:
1236 1230 self.roots.remove(rev)
1237 1231 self.roots.extend(realparents(rev))
1238 1232 return True
1239 1233
1240 1234 return False
1241 1235
1242 1236 # it might be worthwhile to do this in the iterator if the rev range
1243 1237 # is descending and the prune args are all within that range
1244 1238 for rev in opts.get('prune', ()):
1245 1239 rev = repo.changelog.rev(repo.lookup(rev))
1246 1240 ff = followfilter()
1247 1241 stop = min(revs[0], revs[-1])
1248 1242 for x in xrange(rev, stop-1, -1):
1249 1243 if ff.match(x) and x in wanted:
1250 1244 del wanted[x]
1251 1245
1252 1246 def iterate():
1253 1247 if follow and not files:
1254 1248 ff = followfilter(onlyfirst=opts.get('follow_first'))
1255 1249 def want(rev):
1256 1250 if ff.match(rev) and rev in wanted:
1257 1251 return True
1258 1252 return False
1259 1253 else:
1260 1254 def want(rev):
1261 1255 return rev in wanted
1262 1256
1263 1257 for i, window in increasing_windows(0, len(revs)):
1264 1258 yield 'window', revs[0] < revs[-1], revs[-1]
1265 1259 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1266 1260 srevs = list(nrevs)
1267 1261 srevs.sort()
1268 1262 for rev in srevs:
1269 1263 fns = fncache.get(rev)
1270 1264 if not fns:
1271 1265 def fns_generator():
1272 1266 for f in change(rev)[3]:
1273 1267 if matchfn(f):
1274 1268 yield f
1275 1269 fns = fns_generator()
1276 1270 yield 'add', rev, fns
1277 1271 for rev in nrevs:
1278 1272 yield 'iter', rev, None
1279 1273 return iterate(), matchfn
@@ -1,81 +1,79 b''
1 1 #!/bin/sh
2 2
3 3 hg init a
4 4 cd a
5 5 echo a > a
6 6 hg ci -A -d'1 0' -m a
7 7
8 8 cd ..
9 9
10 10 hg init b
11 11 cd b
12 12 echo b > b
13 13 hg ci -A -d'1 0' -m b
14 14
15 15 cd ..
16 16
17 17 hg clone a c
18 18 cd c
19 19 hg pull -f ../b
20 20 hg merge
21 21
22 22 cd ..
23 23
24 24 echo %% -R/--repository
25 25 hg -R a tip
26 26 hg --repository b tip
27 27
28 28 echo %% abbrev of long option
29 29 hg --repo c tip
30 30
31 31 echo "%% earlygetopt with duplicate options (36d23de02da1)"
32 32 hg --cwd a --cwd b --cwd c tip
33 33 hg --repo c --repository b -R a tip
34 34
35 echo "%% earlygetopt short option without spaces (79cc512a34ed)"
36 hg -qR a tip
35 echo "%% earlygetopt short option without following space"
37 36 hg -q -Rb tip
38 hg -qRc tip
39 37
40 38 echo %% --cwd
41 39 hg --cwd a parents
42 40
43 41 echo %% -y/--noninteractive - just be sure it is parsed
44 42 hg --cwd a tip -q --noninteractive
45 43 hg --cwd a tip -q -y
46 44
47 45 echo %% -q/--quiet
48 46 hg -R a -q tip
49 47 hg -R b -q tip
50 48 hg -R c --quiet parents
51 49
52 50 echo %% -v/--verbose
53 51 hg --cwd c head -v
54 52 hg --cwd b tip --verbose
55 53
56 54 echo %% --config
57 55 hg --cwd c --config paths.quuxfoo=bar paths | grep quuxfoo > /dev/null && echo quuxfoo
58 56 hg --cwd c --config '' tip -q
59 57 hg --cwd c --config a.b tip -q
60 58 hg --cwd c --config a tip -q
61 59 hg --cwd c --config a.= tip -q
62 60 hg --cwd c --config .b= tip -q
63 61
64 62 echo %% --debug
65 63 hg --cwd c log --debug
66 64
67 65 echo %% --traceback
68 66 hg --cwd c --config x --traceback tip 2>&1 | grep -i 'traceback'
69 67
70 68 echo %% --time
71 69 hg --cwd a --time tip 2>&1 | grep '^Time:' | sed 's/[0-9][0-9]*/x/g'
72 70
73 71 echo %% --version
74 72 hg --version -q | sed 's/version [^)]*/version xxx/'
75 73
76 74 echo %% -h/--help
77 75 hg -h
78 76 hg --help
79 77
80 78 echo %% not tested: --debugger
81 79
@@ -1,240 +1,238 b''
1 1 adding a
2 2 adding b
3 3 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4 4 pulling from ../b
5 5 searching for changes
6 6 warning: repository is unrelated
7 7 adding changesets
8 8 adding manifests
9 9 adding file changes
10 10 added 1 changesets with 1 changes to 1 files (+1 heads)
11 11 (run 'hg heads' to see heads, 'hg merge' to merge)
12 12 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
13 13 (branch merge, don't forget to commit)
14 14 %% -R/--repository
15 15 changeset: 0:8580ff50825a
16 16 tag: tip
17 17 user: test
18 18 date: Thu Jan 01 00:00:01 1970 +0000
19 19 summary: a
20 20
21 21 changeset: 0:b6c483daf290
22 22 tag: tip
23 23 user: test
24 24 date: Thu Jan 01 00:00:01 1970 +0000
25 25 summary: b
26 26
27 27 %% abbrev of long option
28 28 changeset: 1:b6c483daf290
29 29 tag: tip
30 30 parent: -1:000000000000
31 31 user: test
32 32 date: Thu Jan 01 00:00:01 1970 +0000
33 33 summary: b
34 34
35 35 %% earlygetopt with duplicate options (36d23de02da1)
36 36 changeset: 1:b6c483daf290
37 37 tag: tip
38 38 parent: -1:000000000000
39 39 user: test
40 40 date: Thu Jan 01 00:00:01 1970 +0000
41 41 summary: b
42 42
43 43 changeset: 0:8580ff50825a
44 44 tag: tip
45 45 user: test
46 46 date: Thu Jan 01 00:00:01 1970 +0000
47 47 summary: a
48 48
49 %% earlygetopt short option without spaces (79cc512a34ed)
50 0:8580ff50825a
49 %% earlygetopt short option without following space
51 50 0:b6c483daf290
52 1:b6c483daf290
53 51 %% --cwd
54 52 changeset: 0:8580ff50825a
55 53 tag: tip
56 54 user: test
57 55 date: Thu Jan 01 00:00:01 1970 +0000
58 56 summary: a
59 57
60 58 %% -y/--noninteractive - just be sure it is parsed
61 59 0:8580ff50825a
62 60 0:8580ff50825a
63 61 %% -q/--quiet
64 62 0:8580ff50825a
65 63 0:b6c483daf290
66 64 0:8580ff50825a
67 65 1:b6c483daf290
68 66 %% -v/--verbose
69 67 changeset: 1:b6c483daf290
70 68 tag: tip
71 69 parent: -1:000000000000
72 70 user: test
73 71 date: Thu Jan 01 00:00:01 1970 +0000
74 72 files: b
75 73 description:
76 74 b
77 75
78 76
79 77 changeset: 0:8580ff50825a
80 78 user: test
81 79 date: Thu Jan 01 00:00:01 1970 +0000
82 80 files: a
83 81 description:
84 82 a
85 83
86 84
87 85 changeset: 0:b6c483daf290
88 86 tag: tip
89 87 user: test
90 88 date: Thu Jan 01 00:00:01 1970 +0000
91 89 files: b
92 90 description:
93 91 b
94 92
95 93
96 94 %% --config
97 95 quuxfoo
98 96 abort: malformed --config option:
99 97 abort: malformed --config option: a.b
100 98 abort: malformed --config option: a
101 99 abort: malformed --config option: a.=
102 100 abort: malformed --config option: .b=
103 101 %% --debug
104 102 changeset: 1:b6c483daf2907ce5825c0bb50f5716226281cc1a
105 103 tag: tip
106 104 parent: -1:0000000000000000000000000000000000000000
107 105 parent: -1:0000000000000000000000000000000000000000
108 106 manifest: 1:23226e7a252cacdc2d99e4fbdc3653441056de49
109 107 user: test
110 108 date: Thu Jan 01 00:00:01 1970 +0000
111 109 files+: b
112 110 extra: branch=default
113 111 description:
114 112 b
115 113
116 114
117 115 changeset: 0:8580ff50825a50c8f716709acdf8de0deddcd6ab
118 116 parent: -1:0000000000000000000000000000000000000000
119 117 parent: -1:0000000000000000000000000000000000000000
120 118 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
121 119 user: test
122 120 date: Thu Jan 01 00:00:01 1970 +0000
123 121 files+: a
124 122 extra: branch=default
125 123 description:
126 124 a
127 125
128 126
129 127 %% --traceback
130 128 Traceback (most recent call last):
131 129 %% --time
132 130 Time: real x.x secs (user x.x+x.x sys x.x+x.x)
133 131 %% --version
134 132 Mercurial Distributed SCM (version xxx)
135 133 %% -h/--help
136 134 Mercurial Distributed SCM
137 135
138 136 list of commands:
139 137
140 138 add add the specified files on the next commit
141 139 addremove add all new files, delete all missing files
142 140 annotate show changeset information per file line
143 141 archive create unversioned archive of a repository revision
144 142 backout reverse effect of earlier changeset
145 143 branch set or show the current branch name
146 144 branches list repository named branches
147 145 bundle create a changegroup file
148 146 cat output the current or given revision of files
149 147 clone make a copy of an existing repository
150 148 commit commit the specified files or all outstanding changes
151 149 copy mark files as copied for the next commit
152 150 diff diff repository (or selected files)
153 151 export dump the header and diffs for one or more changesets
154 152 grep search for a pattern in specified files and revisions
155 153 heads show current repository heads or show branch heads
156 154 help show help for a command, extension, or list of commands
157 155 identify identify the working copy or specified revision
158 156 import import an ordered set of patches
159 157 incoming show new changesets found in source
160 158 init create a new repository in the given directory
161 159 locate locate files matching specific patterns
162 160 log show revision history of entire repository or files
163 161 manifest output the current or given revision of the project manifest
164 162 merge merge working directory with another revision
165 163 outgoing show changesets not found in destination
166 164 parents show the parents of the working dir or revision
167 165 paths show definition of symbolic path names
168 166 pull pull changes from the specified source
169 167 push push changes to the specified destination
170 168 recover roll back an interrupted transaction
171 169 remove remove the specified files on the next commit
172 170 rename rename files; equivalent of copy + remove
173 171 revert revert files or dirs to their states as of some revision
174 172 rollback roll back the last transaction in this repository
175 173 root print the root (top) of the current working dir
176 174 serve export the repository via HTTP
177 175 showconfig show combined config settings from all hgrc files
178 176 status show changed files in the working directory
179 177 tag add a tag for the current or given revision
180 178 tags list repository tags
181 179 tip show the tip revision
182 180 unbundle apply one or more changegroup files
183 181 update update working directory
184 182 verify verify the integrity of the repository
185 183 version output version and copyright information
186 184
187 185 use "hg -v help" to show aliases and global options
188 186 Mercurial Distributed SCM
189 187
190 188 list of commands:
191 189
192 190 add add the specified files on the next commit
193 191 addremove add all new files, delete all missing files
194 192 annotate show changeset information per file line
195 193 archive create unversioned archive of a repository revision
196 194 backout reverse effect of earlier changeset
197 195 branch set or show the current branch name
198 196 branches list repository named branches
199 197 bundle create a changegroup file
200 198 cat output the current or given revision of files
201 199 clone make a copy of an existing repository
202 200 commit commit the specified files or all outstanding changes
203 201 copy mark files as copied for the next commit
204 202 diff diff repository (or selected files)
205 203 export dump the header and diffs for one or more changesets
206 204 grep search for a pattern in specified files and revisions
207 205 heads show current repository heads or show branch heads
208 206 help show help for a command, extension, or list of commands
209 207 identify identify the working copy or specified revision
210 208 import import an ordered set of patches
211 209 incoming show new changesets found in source
212 210 init create a new repository in the given directory
213 211 locate locate files matching specific patterns
214 212 log show revision history of entire repository or files
215 213 manifest output the current or given revision of the project manifest
216 214 merge merge working directory with another revision
217 215 outgoing show changesets not found in destination
218 216 parents show the parents of the working dir or revision
219 217 paths show definition of symbolic path names
220 218 pull pull changes from the specified source
221 219 push push changes to the specified destination
222 220 recover roll back an interrupted transaction
223 221 remove remove the specified files on the next commit
224 222 rename rename files; equivalent of copy + remove
225 223 revert revert files or dirs to their states as of some revision
226 224 rollback roll back the last transaction in this repository
227 225 root print the root (top) of the current working dir
228 226 serve export the repository via HTTP
229 227 showconfig show combined config settings from all hgrc files
230 228 status show changed files in the working directory
231 229 tag add a tag for the current or given revision
232 230 tags list repository tags
233 231 tip show the tip revision
234 232 unbundle apply one or more changegroup files
235 233 update update working directory
236 234 verify verify the integrity of the repository
237 235 version output version and copyright information
238 236
239 237 use "hg -v help" to show aliases and global options
240 238 %% not tested: --debugger
General Comments 0
You need to be logged in to leave comments. Login now