##// END OF EJS Templates
log speedup: set quiet/verbose/debugflag in the fake ui object
Alexis S. L. Carvalho -
r3573:31401776 default
parent child Browse files
Show More
@@ -1,3541 +1,3544 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005, 2006 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 demandload import demandload
9 9 from node import *
10 10 from i18n import gettext as _
11 11 demandload(globals(), "os re sys signal shutil imp urllib pdb shlex")
12 12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 13 demandload(globals(), "fnmatch difflib patch random signal tempfile time")
14 14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
15 15 demandload(globals(), "archival cStringIO changegroup")
16 16 demandload(globals(), "cmdutil hgweb.server sshserver")
17 17
18 18 class UnknownCommand(Exception):
19 19 """Exception raised if command is not in the command table."""
20 20 class AmbiguousCommand(Exception):
21 21 """Exception raised if command shortcut matches more than one command."""
22 22
23 23 def bail_if_changed(repo):
24 24 modified, added, removed, deleted = repo.status()[:4]
25 25 if modified or added or removed or deleted:
26 26 raise util.Abort(_("outstanding uncommitted changes"))
27 27
28 28 def relpath(repo, args):
29 29 cwd = repo.getcwd()
30 30 if cwd:
31 31 return [util.normpath(os.path.join(cwd, x)) for x in args]
32 32 return args
33 33
34 34 def logmessage(opts):
35 35 """ get the log message according to -m and -l option """
36 36 message = opts['message']
37 37 logfile = opts['logfile']
38 38
39 39 if message and logfile:
40 40 raise util.Abort(_('options --message and --logfile are mutually '
41 41 'exclusive'))
42 42 if not message and logfile:
43 43 try:
44 44 if logfile == '-':
45 45 message = sys.stdin.read()
46 46 else:
47 47 message = open(logfile).read()
48 48 except IOError, inst:
49 49 raise util.Abort(_("can't read commit message '%s': %s") %
50 50 (logfile, inst.strerror))
51 51 return message
52 52
53 53 def walkchangerevs(ui, repo, pats, change, opts):
54 54 '''Iterate over files and the revs they changed in.
55 55
56 56 Callers most commonly need to iterate backwards over the history
57 57 it is interested in. Doing so has awful (quadratic-looking)
58 58 performance, so we use iterators in a "windowed" way.
59 59
60 60 We walk a window of revisions in the desired order. Within the
61 61 window, we first walk forwards to gather data, then in the desired
62 62 order (usually backwards) to display it.
63 63
64 64 This function returns an (iterator, matchfn) tuple. The iterator
65 65 yields 3-tuples. They will be of one of the following forms:
66 66
67 67 "window", incrementing, lastrev: stepping through a window,
68 68 positive if walking forwards through revs, last rev in the
69 69 sequence iterated over - use to reset state for the current window
70 70
71 71 "add", rev, fns: out-of-order traversal of the given file names
72 72 fns, which changed during revision rev - use to gather data for
73 73 possible display
74 74
75 75 "iter", rev, None: in-order traversal of the revs earlier iterated
76 76 over with "add" - use to display data'''
77 77
78 78 def increasing_windows(start, end, windowsize=8, sizelimit=512):
79 79 if start < end:
80 80 while start < end:
81 81 yield start, min(windowsize, end-start)
82 82 start += windowsize
83 83 if windowsize < sizelimit:
84 84 windowsize *= 2
85 85 else:
86 86 while start > end:
87 87 yield start, min(windowsize, start-end-1)
88 88 start -= windowsize
89 89 if windowsize < sizelimit:
90 90 windowsize *= 2
91 91
92 92 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
93 93 follow = opts.get('follow') or opts.get('follow_first')
94 94
95 95 if repo.changelog.count() == 0:
96 96 return [], matchfn
97 97
98 98 if follow:
99 99 defrange = '%s:0' % repo.changectx().rev()
100 100 else:
101 101 defrange = 'tip:0'
102 102 revs = cmdutil.revrange(ui, repo, opts['rev'] or [defrange])
103 103 wanted = {}
104 104 slowpath = anypats
105 105 fncache = {}
106 106
107 107 if not slowpath and not files:
108 108 # No files, no patterns. Display all revs.
109 109 wanted = dict.fromkeys(revs)
110 110 copies = []
111 111 if not slowpath:
112 112 # Only files, no patterns. Check the history of each file.
113 113 def filerevgen(filelog, node):
114 114 cl_count = repo.changelog.count()
115 115 if node is None:
116 116 last = filelog.count() - 1
117 117 else:
118 118 last = filelog.rev(node)
119 119 for i, window in increasing_windows(last, -1):
120 120 revs = []
121 121 for j in xrange(i - window, i + 1):
122 122 n = filelog.node(j)
123 123 revs.append((filelog.linkrev(n),
124 124 follow and filelog.renamed(n)))
125 125 revs.reverse()
126 126 for rev in revs:
127 127 # only yield rev for which we have the changelog, it can
128 128 # happen while doing "hg log" during a pull or commit
129 129 if rev[0] < cl_count:
130 130 yield rev
131 131 def iterfiles():
132 132 for filename in files:
133 133 yield filename, None
134 134 for filename_node in copies:
135 135 yield filename_node
136 136 minrev, maxrev = min(revs), max(revs)
137 137 for file_, node in iterfiles():
138 138 filelog = repo.file(file_)
139 139 # A zero count may be a directory or deleted file, so
140 140 # try to find matching entries on the slow path.
141 141 if filelog.count() == 0:
142 142 slowpath = True
143 143 break
144 144 for rev, copied in filerevgen(filelog, node):
145 145 if rev <= maxrev:
146 146 if rev < minrev:
147 147 break
148 148 fncache.setdefault(rev, [])
149 149 fncache[rev].append(file_)
150 150 wanted[rev] = 1
151 151 if follow and copied:
152 152 copies.append(copied)
153 153 if slowpath:
154 154 if follow:
155 155 raise util.Abort(_('can only follow copies/renames for explicit '
156 156 'file names'))
157 157
158 158 # The slow path checks files modified in every changeset.
159 159 def changerevgen():
160 160 for i, window in increasing_windows(repo.changelog.count()-1, -1):
161 161 for j in xrange(i - window, i + 1):
162 162 yield j, change(j)[3]
163 163
164 164 for rev, changefiles in changerevgen():
165 165 matches = filter(matchfn, changefiles)
166 166 if matches:
167 167 fncache[rev] = matches
168 168 wanted[rev] = 1
169 169
170 170 class followfilter:
171 171 def __init__(self, onlyfirst=False):
172 172 self.startrev = -1
173 173 self.roots = []
174 174 self.onlyfirst = onlyfirst
175 175
176 176 def match(self, rev):
177 177 def realparents(rev):
178 178 if self.onlyfirst:
179 179 return repo.changelog.parentrevs(rev)[0:1]
180 180 else:
181 181 return filter(lambda x: x != -1, repo.changelog.parentrevs(rev))
182 182
183 183 if self.startrev == -1:
184 184 self.startrev = rev
185 185 return True
186 186
187 187 if rev > self.startrev:
188 188 # forward: all descendants
189 189 if not self.roots:
190 190 self.roots.append(self.startrev)
191 191 for parent in realparents(rev):
192 192 if parent in self.roots:
193 193 self.roots.append(rev)
194 194 return True
195 195 else:
196 196 # backwards: all parents
197 197 if not self.roots:
198 198 self.roots.extend(realparents(self.startrev))
199 199 if rev in self.roots:
200 200 self.roots.remove(rev)
201 201 self.roots.extend(realparents(rev))
202 202 return True
203 203
204 204 return False
205 205
206 206 # it might be worthwhile to do this in the iterator if the rev range
207 207 # is descending and the prune args are all within that range
208 208 for rev in opts.get('prune', ()):
209 209 rev = repo.changelog.rev(repo.lookup(rev))
210 210 ff = followfilter()
211 211 stop = min(revs[0], revs[-1])
212 212 for x in xrange(rev, stop-1, -1):
213 213 if ff.match(x) and x in wanted:
214 214 del wanted[x]
215 215
216 216 def iterate():
217 217 if follow and not files:
218 218 ff = followfilter(onlyfirst=opts.get('follow_first'))
219 219 def want(rev):
220 220 if ff.match(rev) and rev in wanted:
221 221 return True
222 222 return False
223 223 else:
224 224 def want(rev):
225 225 return rev in wanted
226 226
227 227 for i, window in increasing_windows(0, len(revs)):
228 228 yield 'window', revs[0] < revs[-1], revs[-1]
229 229 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
230 230 srevs = list(nrevs)
231 231 srevs.sort()
232 232 for rev in srevs:
233 233 fns = fncache.get(rev)
234 234 if not fns:
235 235 def fns_generator():
236 236 for f in change(rev)[3]:
237 237 if matchfn(f):
238 238 yield f
239 239 fns = fns_generator()
240 240 yield 'add', rev, fns
241 241 for rev in nrevs:
242 242 yield 'iter', rev, None
243 243 return iterate(), matchfn
244 244
245 245 def write_bundle(cg, filename=None, compress=True):
246 246 """Write a bundle file and return its filename.
247 247
248 248 Existing files will not be overwritten.
249 249 If no filename is specified, a temporary file is created.
250 250 bz2 compression can be turned off.
251 251 The bundle file will be deleted in case of errors.
252 252 """
253 253 class nocompress(object):
254 254 def compress(self, x):
255 255 return x
256 256 def flush(self):
257 257 return ""
258 258
259 259 fh = None
260 260 cleanup = None
261 261 try:
262 262 if filename:
263 263 if os.path.exists(filename):
264 264 raise util.Abort(_("file '%s' already exists") % filename)
265 265 fh = open(filename, "wb")
266 266 else:
267 267 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
268 268 fh = os.fdopen(fd, "wb")
269 269 cleanup = filename
270 270
271 271 if compress:
272 272 fh.write("HG10")
273 273 z = bz2.BZ2Compressor(9)
274 274 else:
275 275 fh.write("HG10UN")
276 276 z = nocompress()
277 277 # parse the changegroup data, otherwise we will block
278 278 # in case of sshrepo because we don't know the end of the stream
279 279
280 280 # an empty chunkiter is the end of the changegroup
281 281 empty = False
282 282 while not empty:
283 283 empty = True
284 284 for chunk in changegroup.chunkiter(cg):
285 285 empty = False
286 286 fh.write(z.compress(changegroup.genchunk(chunk)))
287 287 fh.write(z.compress(changegroup.closechunk()))
288 288 fh.write(z.flush())
289 289 cleanup = None
290 290 return filename
291 291 finally:
292 292 if fh is not None:
293 293 fh.close()
294 294 if cleanup is not None:
295 295 os.unlink(cleanup)
296 296
297 297 class changeset_printer(object):
298 298 '''show changeset information when templating not requested.'''
299 299
300 300 def __init__(self, ui, repo):
301 301 self.ui = ui
302 302 self.repo = repo
303 303
304 304 def show(self, rev=0, changenode=None, brinfo=None, copies=None):
305 305 '''show a single changeset or file revision'''
306 306 log = self.repo.changelog
307 307 if changenode is None:
308 308 changenode = log.node(rev)
309 309 elif not rev:
310 310 rev = log.rev(changenode)
311 311
312 312 if self.ui.quiet:
313 313 self.ui.write("%d:%s\n" % (rev, short(changenode)))
314 314 return
315 315
316 316 changes = log.read(changenode)
317 317 date = util.datestr(changes[2])
318 318 extra = changes[5]
319 319 branch = extra.get("branch")
320 320
321 321 hexfunc = self.ui.debugflag and hex or short
322 322
323 323 parents = [(log.rev(p), hexfunc(p)) for p in log.parents(changenode)
324 324 if self.ui.debugflag or p != nullid]
325 325 if (not self.ui.debugflag and len(parents) == 1 and
326 326 parents[0][0] == rev-1):
327 327 parents = []
328 328
329 329 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
330 330
331 331 if branch:
332 332 self.ui.write(_("branch: %s\n") % branch)
333 333 for tag in self.repo.nodetags(changenode):
334 334 self.ui.write(_("tag: %s\n") % tag)
335 335 for parent in parents:
336 336 self.ui.write(_("parent: %d:%s\n") % parent)
337 337
338 338 if brinfo and changenode in brinfo:
339 339 br = brinfo[changenode]
340 340 self.ui.write(_("branch: %s\n") % " ".join(br))
341 341
342 342 if self.ui.debugflag:
343 343 self.ui.write(_("manifest: %d:%s\n") %
344 344 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
345 345 self.ui.write(_("user: %s\n") % changes[1])
346 346 self.ui.write(_("date: %s\n") % date)
347 347
348 348 if self.ui.debugflag:
349 349 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
350 350 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
351 351 files):
352 352 if value:
353 353 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
354 354 elif changes[3] and self.ui.verbose:
355 355 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
356 356 if copies and self.ui.verbose:
357 357 copies = ['%s (%s)' % c for c in copies]
358 358 self.ui.write(_("copies: %s\n") % ' '.join(copies))
359 359
360 360 if extra and self.ui.debugflag:
361 361 extraitems = extra.items()
362 362 extraitems.sort()
363 363 for key, value in extraitems:
364 364 self.ui.write(_("extra: %s=%s\n")
365 365 % (key, value.encode('string_escape')))
366 366
367 367 description = changes[4].strip()
368 368 if description:
369 369 if self.ui.verbose:
370 370 self.ui.write(_("description:\n"))
371 371 self.ui.write(description)
372 372 self.ui.write("\n\n")
373 373 else:
374 374 self.ui.write(_("summary: %s\n") %
375 375 description.splitlines()[0])
376 376 self.ui.write("\n")
377 377
378 378 def show_changeset(ui, repo, opts):
379 379 """show one changeset using template or regular display.
380 380
381 381 Display format will be the first non-empty hit of:
382 382 1. option 'template'
383 383 2. option 'style'
384 384 3. [ui] setting 'logtemplate'
385 385 4. [ui] setting 'style'
386 386 If all of these values are either the unset or the empty string,
387 387 regular display via changeset_printer() is done.
388 388 """
389 389 # options
390 390 tmpl = opts.get('template')
391 391 mapfile = None
392 392 if tmpl:
393 393 tmpl = templater.parsestring(tmpl, quoted=False)
394 394 else:
395 395 mapfile = opts.get('style')
396 396 # ui settings
397 397 if not mapfile:
398 398 tmpl = ui.config('ui', 'logtemplate')
399 399 if tmpl:
400 400 tmpl = templater.parsestring(tmpl)
401 401 else:
402 402 mapfile = ui.config('ui', 'style')
403 403
404 404 if tmpl or mapfile:
405 405 if mapfile:
406 406 if not os.path.split(mapfile)[0]:
407 407 mapname = (templater.templatepath('map-cmdline.' + mapfile)
408 408 or templater.templatepath(mapfile))
409 409 if mapname: mapfile = mapname
410 410 try:
411 411 t = templater.changeset_templater(ui, repo, mapfile)
412 412 except SyntaxError, inst:
413 413 raise util.Abort(inst.args[0])
414 414 if tmpl: t.use_template(tmpl)
415 415 return t
416 416 return changeset_printer(ui, repo)
417 417
418 418 def setremoteconfig(ui, opts):
419 419 "copy remote options to ui tree"
420 420 if opts.get('ssh'):
421 421 ui.setconfig("ui", "ssh", opts['ssh'])
422 422 if opts.get('remotecmd'):
423 423 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
424 424
425 425 def show_version(ui):
426 426 """output version and copyright information"""
427 427 ui.write(_("Mercurial Distributed SCM (version %s)\n")
428 428 % version.get_version())
429 429 ui.status(_(
430 430 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
431 431 "This is free software; see the source for copying conditions. "
432 432 "There is NO\nwarranty; "
433 433 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
434 434 ))
435 435
436 436 def help_(ui, name=None, with_version=False):
437 437 """show help for a command, extension, or list of commands
438 438
439 439 With no arguments, print a list of commands and short help.
440 440
441 441 Given a command name, print help for that command.
442 442
443 443 Given an extension name, print help for that extension, and the
444 444 commands it provides."""
445 445 option_lists = []
446 446
447 447 def helpcmd(name):
448 448 if with_version:
449 449 show_version(ui)
450 450 ui.write('\n')
451 451 aliases, i = findcmd(ui, name)
452 452 # synopsis
453 453 ui.write("%s\n\n" % i[2])
454 454
455 455 # description
456 456 doc = i[0].__doc__
457 457 if not doc:
458 458 doc = _("(No help text available)")
459 459 if ui.quiet:
460 460 doc = doc.splitlines(0)[0]
461 461 ui.write("%s\n" % doc.rstrip())
462 462
463 463 if not ui.quiet:
464 464 # aliases
465 465 if len(aliases) > 1:
466 466 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
467 467
468 468 # options
469 469 if i[1]:
470 470 option_lists.append(("options", i[1]))
471 471
472 472 def helplist(select=None):
473 473 h = {}
474 474 cmds = {}
475 475 for c, e in table.items():
476 476 f = c.split("|", 1)[0]
477 477 if select and not select(f):
478 478 continue
479 479 if name == "shortlist" and not f.startswith("^"):
480 480 continue
481 481 f = f.lstrip("^")
482 482 if not ui.debugflag and f.startswith("debug"):
483 483 continue
484 484 doc = e[0].__doc__
485 485 if not doc:
486 486 doc = _("(No help text available)")
487 487 h[f] = doc.splitlines(0)[0].rstrip()
488 488 cmds[f] = c.lstrip("^")
489 489
490 490 fns = h.keys()
491 491 fns.sort()
492 492 m = max(map(len, fns))
493 493 for f in fns:
494 494 if ui.verbose:
495 495 commands = cmds[f].replace("|",", ")
496 496 ui.write(" %s:\n %s\n"%(commands, h[f]))
497 497 else:
498 498 ui.write(' %-*s %s\n' % (m, f, h[f]))
499 499
500 500 def helpext(name):
501 501 try:
502 502 mod = findext(name)
503 503 except KeyError:
504 504 raise UnknownCommand(name)
505 505
506 506 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
507 507 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
508 508 for d in doc[1:]:
509 509 ui.write(d, '\n')
510 510
511 511 ui.status('\n')
512 512 if ui.verbose:
513 513 ui.status(_('list of commands:\n\n'))
514 514 else:
515 515 ui.status(_('list of commands (use "hg help -v %s" '
516 516 'to show aliases and global options):\n\n') % name)
517 517
518 518 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
519 519 helplist(modcmds.has_key)
520 520
521 521 if name and name != 'shortlist':
522 522 try:
523 523 helpcmd(name)
524 524 except UnknownCommand:
525 525 helpext(name)
526 526
527 527 else:
528 528 # program name
529 529 if ui.verbose or with_version:
530 530 show_version(ui)
531 531 else:
532 532 ui.status(_("Mercurial Distributed SCM\n"))
533 533 ui.status('\n')
534 534
535 535 # list of commands
536 536 if name == "shortlist":
537 537 ui.status(_('basic commands (use "hg help" '
538 538 'for the full list or option "-v" for details):\n\n'))
539 539 elif ui.verbose:
540 540 ui.status(_('list of commands:\n\n'))
541 541 else:
542 542 ui.status(_('list of commands (use "hg help -v" '
543 543 'to show aliases and global options):\n\n'))
544 544
545 545 helplist()
546 546
547 547 # global options
548 548 if ui.verbose:
549 549 option_lists.append(("global options", globalopts))
550 550
551 551 # list all option lists
552 552 opt_output = []
553 553 for title, options in option_lists:
554 554 opt_output.append(("\n%s:\n" % title, None))
555 555 for shortopt, longopt, default, desc in options:
556 556 if "DEPRECATED" in desc and not ui.verbose: continue
557 557 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
558 558 longopt and " --%s" % longopt),
559 559 "%s%s" % (desc,
560 560 default
561 561 and _(" (default: %s)") % default
562 562 or "")))
563 563
564 564 if opt_output:
565 565 opts_len = max([len(line[0]) for line in opt_output if line[1]])
566 566 for first, second in opt_output:
567 567 if second:
568 568 ui.write(" %-*s %s\n" % (opts_len, first, second))
569 569 else:
570 570 ui.write("%s\n" % first)
571 571
572 572 # Commands start here, listed alphabetically
573 573
574 574 def add(ui, repo, *pats, **opts):
575 575 """add the specified files on the next commit
576 576
577 577 Schedule files to be version controlled and added to the repository.
578 578
579 579 The files will be added to the repository at the next commit.
580 580
581 581 If no names are given, add all files in the repository.
582 582 """
583 583
584 584 names = []
585 585 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
586 586 if exact:
587 587 if ui.verbose:
588 588 ui.status(_('adding %s\n') % rel)
589 589 names.append(abs)
590 590 elif repo.dirstate.state(abs) == '?':
591 591 ui.status(_('adding %s\n') % rel)
592 592 names.append(abs)
593 593 if not opts.get('dry_run'):
594 594 repo.add(names)
595 595
596 596 def addremove(ui, repo, *pats, **opts):
597 597 """add all new files, delete all missing files
598 598
599 599 Add all new files and remove all missing files from the repository.
600 600
601 601 New files are ignored if they match any of the patterns in .hgignore. As
602 602 with add, these changes take effect at the next commit.
603 603
604 604 Use the -s option to detect renamed files. With a parameter > 0,
605 605 this compares every removed file with every added file and records
606 606 those similar enough as renames. This option takes a percentage
607 607 between 0 (disabled) and 100 (files must be identical) as its
608 608 parameter. Detecting renamed files this way can be expensive.
609 609 """
610 610 sim = float(opts.get('similarity') or 0)
611 611 if sim < 0 or sim > 100:
612 612 raise util.Abort(_('similarity must be between 0 and 100'))
613 613 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
614 614
615 615 def annotate(ui, repo, *pats, **opts):
616 616 """show changeset information per file line
617 617
618 618 List changes in files, showing the revision id responsible for each line
619 619
620 620 This command is useful to discover who did a change or when a change took
621 621 place.
622 622
623 623 Without the -a option, annotate will avoid processing files it
624 624 detects as binary. With -a, annotate will generate an annotation
625 625 anyway, probably with undesirable results.
626 626 """
627 627 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
628 628
629 629 if not pats:
630 630 raise util.Abort(_('at least one file name or pattern required'))
631 631
632 632 opmap = [['user', lambda x: ui.shortuser(x.user())],
633 633 ['number', lambda x: str(x.rev())],
634 634 ['changeset', lambda x: short(x.node())],
635 635 ['date', getdate], ['follow', lambda x: x.path()]]
636 636 if (not opts['user'] and not opts['changeset'] and not opts['date']
637 637 and not opts['follow']):
638 638 opts['number'] = 1
639 639
640 640 ctx = repo.changectx(opts['rev'])
641 641
642 642 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
643 643 node=ctx.node()):
644 644 fctx = ctx.filectx(abs)
645 645 if not opts['text'] and util.binary(fctx.data()):
646 646 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
647 647 continue
648 648
649 649 lines = fctx.annotate(follow=opts.get('follow'))
650 650 pieces = []
651 651
652 652 for o, f in opmap:
653 653 if opts[o]:
654 654 l = [f(n) for n, dummy in lines]
655 655 if l:
656 656 m = max(map(len, l))
657 657 pieces.append(["%*s" % (m, x) for x in l])
658 658
659 659 if pieces:
660 660 for p, l in zip(zip(*pieces), lines):
661 661 ui.write("%s: %s" % (" ".join(p), l[1]))
662 662
663 663 def archive(ui, repo, dest, **opts):
664 664 '''create unversioned archive of a repository revision
665 665
666 666 By default, the revision used is the parent of the working
667 667 directory; use "-r" to specify a different revision.
668 668
669 669 To specify the type of archive to create, use "-t". Valid
670 670 types are:
671 671
672 672 "files" (default): a directory full of files
673 673 "tar": tar archive, uncompressed
674 674 "tbz2": tar archive, compressed using bzip2
675 675 "tgz": tar archive, compressed using gzip
676 676 "uzip": zip archive, uncompressed
677 677 "zip": zip archive, compressed using deflate
678 678
679 679 The exact name of the destination archive or directory is given
680 680 using a format string; see "hg help export" for details.
681 681
682 682 Each member added to an archive file has a directory prefix
683 683 prepended. Use "-p" to specify a format string for the prefix.
684 684 The default is the basename of the archive, with suffixes removed.
685 685 '''
686 686
687 687 node = repo.changectx(opts['rev']).node()
688 688 dest = cmdutil.make_filename(repo, dest, node)
689 689 if os.path.realpath(dest) == repo.root:
690 690 raise util.Abort(_('repository root cannot be destination'))
691 691 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
692 692 kind = opts.get('type') or 'files'
693 693 prefix = opts['prefix']
694 694 if dest == '-':
695 695 if kind == 'files':
696 696 raise util.Abort(_('cannot archive plain files to stdout'))
697 697 dest = sys.stdout
698 698 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
699 699 prefix = cmdutil.make_filename(repo, prefix, node)
700 700 archival.archive(repo, dest, node, kind, not opts['no_decode'],
701 701 matchfn, prefix)
702 702
703 703 def backout(ui, repo, rev, **opts):
704 704 '''reverse effect of earlier changeset
705 705
706 706 Commit the backed out changes as a new changeset. The new
707 707 changeset is a child of the backed out changeset.
708 708
709 709 If you back out a changeset other than the tip, a new head is
710 710 created. This head is the parent of the working directory. If
711 711 you back out an old changeset, your working directory will appear
712 712 old after the backout. You should merge the backout changeset
713 713 with another head.
714 714
715 715 The --merge option remembers the parent of the working directory
716 716 before starting the backout, then merges the new head with that
717 717 changeset afterwards. This saves you from doing the merge by
718 718 hand. The result of this merge is not committed, as for a normal
719 719 merge.'''
720 720
721 721 bail_if_changed(repo)
722 722 op1, op2 = repo.dirstate.parents()
723 723 if op2 != nullid:
724 724 raise util.Abort(_('outstanding uncommitted merge'))
725 725 node = repo.lookup(rev)
726 726 p1, p2 = repo.changelog.parents(node)
727 727 if p1 == nullid:
728 728 raise util.Abort(_('cannot back out a change with no parents'))
729 729 if p2 != nullid:
730 730 if not opts['parent']:
731 731 raise util.Abort(_('cannot back out a merge changeset without '
732 732 '--parent'))
733 733 p = repo.lookup(opts['parent'])
734 734 if p not in (p1, p2):
735 735 raise util.Abort(_('%s is not a parent of %s' %
736 736 (short(p), short(node))))
737 737 parent = p
738 738 else:
739 739 if opts['parent']:
740 740 raise util.Abort(_('cannot use --parent on non-merge changeset'))
741 741 parent = p1
742 742 hg.clean(repo, node, show_stats=False)
743 743 revert_opts = opts.copy()
744 744 revert_opts['all'] = True
745 745 revert_opts['rev'] = hex(parent)
746 746 revert(ui, repo, **revert_opts)
747 747 commit_opts = opts.copy()
748 748 commit_opts['addremove'] = False
749 749 if not commit_opts['message'] and not commit_opts['logfile']:
750 750 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
751 751 commit_opts['force_editor'] = True
752 752 commit(ui, repo, **commit_opts)
753 753 def nice(node):
754 754 return '%d:%s' % (repo.changelog.rev(node), short(node))
755 755 ui.status(_('changeset %s backs out changeset %s\n') %
756 756 (nice(repo.changelog.tip()), nice(node)))
757 757 if op1 != node:
758 758 if opts['merge']:
759 759 ui.status(_('merging with changeset %s\n') % nice(op1))
760 760 n = _lookup(repo, hex(op1))
761 761 hg.merge(repo, n)
762 762 else:
763 763 ui.status(_('the backout changeset is a new head - '
764 764 'do not forget to merge\n'))
765 765 ui.status(_('(use "backout --merge" '
766 766 'if you want to auto-merge)\n'))
767 767
768 768 def branch(ui, repo, label=None):
769 769 """set or show the current branch name
770 770
771 771 With <name>, set the current branch name. Otherwise, show the
772 772 current branch name.
773 773 """
774 774
775 775 if label is not None:
776 776 repo.opener("branch", "w").write(label)
777 777 else:
778 778 b = repo.workingctx().branch()
779 779 if b:
780 780 ui.write("%s\n" % b)
781 781
782 782 def branches(ui, repo):
783 783 """list repository named branches
784 784
785 785 List the repository's named branches.
786 786 """
787 787 b = repo.branchtags()
788 788 l = [(-repo.changelog.rev(n), n, t) for t,n in b.items()]
789 789 l.sort()
790 790 for r, n, t in l:
791 791 hexfunc = ui.debugflag and hex or short
792 792 if ui.quiet:
793 793 ui.write("%s\n" % t)
794 794 else:
795 795 ui.write("%-30s %s:%s\n" % (t, -r, hexfunc(n)))
796 796
797 797 def bundle(ui, repo, fname, dest=None, **opts):
798 798 """create a changegroup file
799 799
800 800 Generate a compressed changegroup file collecting changesets not
801 801 found in the other repository.
802 802
803 803 If no destination repository is specified the destination is assumed
804 804 to have all the nodes specified by one or more --base parameters.
805 805
806 806 The bundle file can then be transferred using conventional means and
807 807 applied to another repository with the unbundle or pull command.
808 808 This is useful when direct push and pull are not available or when
809 809 exporting an entire repository is undesirable.
810 810
811 811 Applying bundles preserves all changeset contents including
812 812 permissions, copy/rename information, and revision history.
813 813 """
814 814 revs = opts.get('rev') or None
815 815 if revs:
816 816 revs = [repo.lookup(rev) for rev in revs]
817 817 base = opts.get('base')
818 818 if base:
819 819 if dest:
820 820 raise util.Abort(_("--base is incompatible with specifiying "
821 821 "a destination"))
822 822 base = [repo.lookup(rev) for rev in base]
823 823 # create the right base
824 824 # XXX: nodesbetween / changegroup* should be "fixed" instead
825 825 o = []
826 826 has_set = sets.Set(base)
827 827 for n in base:
828 828 has_set.update(repo.changelog.reachable(n))
829 829 if revs:
830 830 visit = list(revs)
831 831 else:
832 832 visit = repo.changelog.heads()
833 833 seen = sets.Set(visit)
834 834 while visit:
835 835 n = visit.pop(0)
836 836 parents = [p for p in repo.changelog.parents(n)
837 837 if p != nullid and p not in has_set]
838 838 if len(parents) == 0:
839 839 o.insert(0, n)
840 840 else:
841 841 for p in parents:
842 842 if p not in seen:
843 843 seen.add(p)
844 844 visit.append(p)
845 845 else:
846 846 setremoteconfig(ui, opts)
847 847 dest = ui.expandpath(dest or 'default-push', dest or 'default')
848 848 other = hg.repository(ui, dest)
849 849 o = repo.findoutgoing(other, force=opts['force'])
850 850
851 851 if revs:
852 852 cg = repo.changegroupsubset(o, revs, 'bundle')
853 853 else:
854 854 cg = repo.changegroup(o, 'bundle')
855 855 write_bundle(cg, fname)
856 856
857 857 def cat(ui, repo, file1, *pats, **opts):
858 858 """output the latest or given revisions of files
859 859
860 860 Print the specified files as they were at the given revision.
861 861 If no revision is given then working dir parent is used, or tip
862 862 if no revision is checked out.
863 863
864 864 Output may be to a file, in which case the name of the file is
865 865 given using a format string. The formatting rules are the same as
866 866 for the export command, with the following additions:
867 867
868 868 %s basename of file being printed
869 869 %d dirname of file being printed, or '.' if in repo root
870 870 %p root-relative path name of file being printed
871 871 """
872 872 ctx = repo.changectx(opts['rev'])
873 873 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
874 874 ctx.node()):
875 875 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
876 876 fp.write(ctx.filectx(abs).data())
877 877
878 878 def clone(ui, source, dest=None, **opts):
879 879 """make a copy of an existing repository
880 880
881 881 Create a copy of an existing repository in a new directory.
882 882
883 883 If no destination directory name is specified, it defaults to the
884 884 basename of the source.
885 885
886 886 The location of the source is added to the new repository's
887 887 .hg/hgrc file, as the default to be used for future pulls.
888 888
889 889 For efficiency, hardlinks are used for cloning whenever the source
890 890 and destination are on the same filesystem (note this applies only
891 891 to the repository data, not to the checked out files). Some
892 892 filesystems, such as AFS, implement hardlinking incorrectly, but
893 893 do not report errors. In these cases, use the --pull option to
894 894 avoid hardlinking.
895 895
896 896 You can safely clone repositories and checked out files using full
897 897 hardlinks with
898 898
899 899 $ cp -al REPO REPOCLONE
900 900
901 901 which is the fastest way to clone. However, the operation is not
902 902 atomic (making sure REPO is not modified during the operation is
903 903 up to you) and you have to make sure your editor breaks hardlinks
904 904 (Emacs and most Linux Kernel tools do so).
905 905
906 906 If you use the -r option to clone up to a specific revision, no
907 907 subsequent revisions will be present in the cloned repository.
908 908 This option implies --pull, even on local repositories.
909 909
910 910 See pull for valid source format details.
911 911
912 912 It is possible to specify an ssh:// URL as the destination, but no
913 913 .hg/hgrc will be created on the remote side. Look at the help text
914 914 for the pull command for important details about ssh:// URLs.
915 915 """
916 916 setremoteconfig(ui, opts)
917 917 hg.clone(ui, ui.expandpath(source), dest,
918 918 pull=opts['pull'],
919 919 stream=opts['uncompressed'],
920 920 rev=opts['rev'],
921 921 update=not opts['noupdate'])
922 922
923 923 def commit(ui, repo, *pats, **opts):
924 924 """commit the specified files or all outstanding changes
925 925
926 926 Commit changes to the given files into the repository.
927 927
928 928 If a list of files is omitted, all changes reported by "hg status"
929 929 will be committed.
930 930
931 931 If no commit message is specified, the editor configured in your hgrc
932 932 or in the EDITOR environment variable is started to enter a message.
933 933 """
934 934 message = logmessage(opts)
935 935
936 936 if opts['addremove']:
937 937 cmdutil.addremove(repo, pats, opts)
938 938 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
939 939 if pats:
940 940 modified, added, removed = repo.status(files=fns, match=match)[:3]
941 941 files = modified + added + removed
942 942 else:
943 943 files = []
944 944 try:
945 945 repo.commit(files, message, opts['user'], opts['date'], match,
946 946 force_editor=opts.get('force_editor'))
947 947 except ValueError, inst:
948 948 raise util.Abort(str(inst))
949 949
950 950 def docopy(ui, repo, pats, opts, wlock):
951 951 # called with the repo lock held
952 952 cwd = repo.getcwd()
953 953 errors = 0
954 954 copied = []
955 955 targets = {}
956 956
957 957 def okaytocopy(abs, rel, exact):
958 958 reasons = {'?': _('is not managed'),
959 959 'a': _('has been marked for add'),
960 960 'r': _('has been marked for remove')}
961 961 state = repo.dirstate.state(abs)
962 962 reason = reasons.get(state)
963 963 if reason:
964 964 if state == 'a':
965 965 origsrc = repo.dirstate.copied(abs)
966 966 if origsrc is not None:
967 967 return origsrc
968 968 if exact:
969 969 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
970 970 else:
971 971 return abs
972 972
973 973 def copy(origsrc, abssrc, relsrc, target, exact):
974 974 abstarget = util.canonpath(repo.root, cwd, target)
975 975 reltarget = util.pathto(cwd, abstarget)
976 976 prevsrc = targets.get(abstarget)
977 977 if prevsrc is not None:
978 978 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
979 979 (reltarget, abssrc, prevsrc))
980 980 return
981 981 if (not opts['after'] and os.path.exists(reltarget) or
982 982 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
983 983 if not opts['force']:
984 984 ui.warn(_('%s: not overwriting - file exists\n') %
985 985 reltarget)
986 986 return
987 987 if not opts['after'] and not opts.get('dry_run'):
988 988 os.unlink(reltarget)
989 989 if opts['after']:
990 990 if not os.path.exists(reltarget):
991 991 return
992 992 else:
993 993 targetdir = os.path.dirname(reltarget) or '.'
994 994 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
995 995 os.makedirs(targetdir)
996 996 try:
997 997 restore = repo.dirstate.state(abstarget) == 'r'
998 998 if restore and not opts.get('dry_run'):
999 999 repo.undelete([abstarget], wlock)
1000 1000 try:
1001 1001 if not opts.get('dry_run'):
1002 1002 shutil.copyfile(relsrc, reltarget)
1003 1003 shutil.copymode(relsrc, reltarget)
1004 1004 restore = False
1005 1005 finally:
1006 1006 if restore:
1007 1007 repo.remove([abstarget], wlock)
1008 1008 except shutil.Error, inst:
1009 1009 raise util.Abort(str(inst))
1010 1010 except IOError, inst:
1011 1011 if inst.errno == errno.ENOENT:
1012 1012 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1013 1013 else:
1014 1014 ui.warn(_('%s: cannot copy - %s\n') %
1015 1015 (relsrc, inst.strerror))
1016 1016 errors += 1
1017 1017 return
1018 1018 if ui.verbose or not exact:
1019 1019 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1020 1020 targets[abstarget] = abssrc
1021 1021 if abstarget != origsrc and not opts.get('dry_run'):
1022 1022 repo.copy(origsrc, abstarget, wlock)
1023 1023 copied.append((abssrc, relsrc, exact))
1024 1024
1025 1025 def targetpathfn(pat, dest, srcs):
1026 1026 if os.path.isdir(pat):
1027 1027 abspfx = util.canonpath(repo.root, cwd, pat)
1028 1028 if destdirexists:
1029 1029 striplen = len(os.path.split(abspfx)[0])
1030 1030 else:
1031 1031 striplen = len(abspfx)
1032 1032 if striplen:
1033 1033 striplen += len(os.sep)
1034 1034 res = lambda p: os.path.join(dest, p[striplen:])
1035 1035 elif destdirexists:
1036 1036 res = lambda p: os.path.join(dest, os.path.basename(p))
1037 1037 else:
1038 1038 res = lambda p: dest
1039 1039 return res
1040 1040
1041 1041 def targetpathafterfn(pat, dest, srcs):
1042 1042 if util.patkind(pat, None)[0]:
1043 1043 # a mercurial pattern
1044 1044 res = lambda p: os.path.join(dest, os.path.basename(p))
1045 1045 else:
1046 1046 abspfx = util.canonpath(repo.root, cwd, pat)
1047 1047 if len(abspfx) < len(srcs[0][0]):
1048 1048 # A directory. Either the target path contains the last
1049 1049 # component of the source path or it does not.
1050 1050 def evalpath(striplen):
1051 1051 score = 0
1052 1052 for s in srcs:
1053 1053 t = os.path.join(dest, s[0][striplen:])
1054 1054 if os.path.exists(t):
1055 1055 score += 1
1056 1056 return score
1057 1057
1058 1058 striplen = len(abspfx)
1059 1059 if striplen:
1060 1060 striplen += len(os.sep)
1061 1061 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1062 1062 score = evalpath(striplen)
1063 1063 striplen1 = len(os.path.split(abspfx)[0])
1064 1064 if striplen1:
1065 1065 striplen1 += len(os.sep)
1066 1066 if evalpath(striplen1) > score:
1067 1067 striplen = striplen1
1068 1068 res = lambda p: os.path.join(dest, p[striplen:])
1069 1069 else:
1070 1070 # a file
1071 1071 if destdirexists:
1072 1072 res = lambda p: os.path.join(dest, os.path.basename(p))
1073 1073 else:
1074 1074 res = lambda p: dest
1075 1075 return res
1076 1076
1077 1077
1078 1078 pats = list(pats)
1079 1079 if not pats:
1080 1080 raise util.Abort(_('no source or destination specified'))
1081 1081 if len(pats) == 1:
1082 1082 raise util.Abort(_('no destination specified'))
1083 1083 dest = pats.pop()
1084 1084 destdirexists = os.path.isdir(dest)
1085 1085 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1086 1086 raise util.Abort(_('with multiple sources, destination must be an '
1087 1087 'existing directory'))
1088 1088 if opts['after']:
1089 1089 tfn = targetpathafterfn
1090 1090 else:
1091 1091 tfn = targetpathfn
1092 1092 copylist = []
1093 1093 for pat in pats:
1094 1094 srcs = []
1095 1095 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
1096 1096 origsrc = okaytocopy(abssrc, relsrc, exact)
1097 1097 if origsrc:
1098 1098 srcs.append((origsrc, abssrc, relsrc, exact))
1099 1099 if not srcs:
1100 1100 continue
1101 1101 copylist.append((tfn(pat, dest, srcs), srcs))
1102 1102 if not copylist:
1103 1103 raise util.Abort(_('no files to copy'))
1104 1104
1105 1105 for targetpath, srcs in copylist:
1106 1106 for origsrc, abssrc, relsrc, exact in srcs:
1107 1107 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1108 1108
1109 1109 if errors:
1110 1110 ui.warn(_('(consider using --after)\n'))
1111 1111 return errors, copied
1112 1112
1113 1113 def copy(ui, repo, *pats, **opts):
1114 1114 """mark files as copied for the next commit
1115 1115
1116 1116 Mark dest as having copies of source files. If dest is a
1117 1117 directory, copies are put in that directory. If dest is a file,
1118 1118 there can only be one source.
1119 1119
1120 1120 By default, this command copies the contents of files as they
1121 1121 stand in the working directory. If invoked with --after, the
1122 1122 operation is recorded, but no copying is performed.
1123 1123
1124 1124 This command takes effect in the next commit.
1125 1125
1126 1126 NOTE: This command should be treated as experimental. While it
1127 1127 should properly record copied files, this information is not yet
1128 1128 fully used by merge, nor fully reported by log.
1129 1129 """
1130 1130 wlock = repo.wlock(0)
1131 1131 errs, copied = docopy(ui, repo, pats, opts, wlock)
1132 1132 return errs
1133 1133
1134 1134 def debugancestor(ui, index, rev1, rev2):
1135 1135 """find the ancestor revision of two revisions in a given index"""
1136 1136 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1137 1137 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1138 1138 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1139 1139
1140 1140 def debugcomplete(ui, cmd='', **opts):
1141 1141 """returns the completion list associated with the given command"""
1142 1142
1143 1143 if opts['options']:
1144 1144 options = []
1145 1145 otables = [globalopts]
1146 1146 if cmd:
1147 1147 aliases, entry = findcmd(ui, cmd)
1148 1148 otables.append(entry[1])
1149 1149 for t in otables:
1150 1150 for o in t:
1151 1151 if o[0]:
1152 1152 options.append('-%s' % o[0])
1153 1153 options.append('--%s' % o[1])
1154 1154 ui.write("%s\n" % "\n".join(options))
1155 1155 return
1156 1156
1157 1157 clist = findpossible(ui, cmd).keys()
1158 1158 clist.sort()
1159 1159 ui.write("%s\n" % "\n".join(clist))
1160 1160
1161 1161 def debugrebuildstate(ui, repo, rev=None):
1162 1162 """rebuild the dirstate as it would look like for the given revision"""
1163 1163 if not rev:
1164 1164 rev = repo.changelog.tip()
1165 1165 else:
1166 1166 rev = repo.lookup(rev)
1167 1167 change = repo.changelog.read(rev)
1168 1168 n = change[0]
1169 1169 files = repo.manifest.read(n)
1170 1170 wlock = repo.wlock()
1171 1171 repo.dirstate.rebuild(rev, files)
1172 1172
1173 1173 def debugcheckstate(ui, repo):
1174 1174 """validate the correctness of the current dirstate"""
1175 1175 parent1, parent2 = repo.dirstate.parents()
1176 1176 repo.dirstate.read()
1177 1177 dc = repo.dirstate.map
1178 1178 keys = dc.keys()
1179 1179 keys.sort()
1180 1180 m1n = repo.changelog.read(parent1)[0]
1181 1181 m2n = repo.changelog.read(parent2)[0]
1182 1182 m1 = repo.manifest.read(m1n)
1183 1183 m2 = repo.manifest.read(m2n)
1184 1184 errors = 0
1185 1185 for f in dc:
1186 1186 state = repo.dirstate.state(f)
1187 1187 if state in "nr" and f not in m1:
1188 1188 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1189 1189 errors += 1
1190 1190 if state in "a" and f in m1:
1191 1191 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1192 1192 errors += 1
1193 1193 if state in "m" and f not in m1 and f not in m2:
1194 1194 ui.warn(_("%s in state %s, but not in either manifest\n") %
1195 1195 (f, state))
1196 1196 errors += 1
1197 1197 for f in m1:
1198 1198 state = repo.dirstate.state(f)
1199 1199 if state not in "nrm":
1200 1200 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1201 1201 errors += 1
1202 1202 if errors:
1203 1203 error = _(".hg/dirstate inconsistent with current parent's manifest")
1204 1204 raise util.Abort(error)
1205 1205
1206 1206 def showconfig(ui, repo, *values, **opts):
1207 1207 """show combined config settings from all hgrc files
1208 1208
1209 1209 With no args, print names and values of all config items.
1210 1210
1211 1211 With one arg of the form section.name, print just the value of
1212 1212 that config item.
1213 1213
1214 1214 With multiple args, print names and values of all config items
1215 1215 with matching section names."""
1216 1216
1217 1217 untrusted = bool(opts.get('untrusted'))
1218 1218 if values:
1219 1219 if len([v for v in values if '.' in v]) > 1:
1220 1220 raise util.Abort(_('only one config item permitted'))
1221 1221 for section, name, value in ui.walkconfig(untrusted=untrusted):
1222 1222 sectname = section + '.' + name
1223 1223 if values:
1224 1224 for v in values:
1225 1225 if v == section:
1226 1226 ui.write('%s=%s\n' % (sectname, value))
1227 1227 elif v == sectname:
1228 1228 ui.write(value, '\n')
1229 1229 else:
1230 1230 ui.write('%s=%s\n' % (sectname, value))
1231 1231
1232 1232 def debugsetparents(ui, repo, rev1, rev2=None):
1233 1233 """manually set the parents of the current working directory
1234 1234
1235 1235 This is useful for writing repository conversion tools, but should
1236 1236 be used with care.
1237 1237 """
1238 1238
1239 1239 if not rev2:
1240 1240 rev2 = hex(nullid)
1241 1241
1242 1242 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1243 1243
1244 1244 def debugstate(ui, repo):
1245 1245 """show the contents of the current dirstate"""
1246 1246 repo.dirstate.read()
1247 1247 dc = repo.dirstate.map
1248 1248 keys = dc.keys()
1249 1249 keys.sort()
1250 1250 for file_ in keys:
1251 1251 ui.write("%c %3o %10d %s %s\n"
1252 1252 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1253 1253 time.strftime("%x %X",
1254 1254 time.localtime(dc[file_][3])), file_))
1255 1255 for f in repo.dirstate.copies():
1256 1256 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1257 1257
1258 1258 def debugdata(ui, file_, rev):
1259 1259 """dump the contents of an data file revision"""
1260 1260 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1261 1261 file_[:-2] + ".i", file_, 0)
1262 1262 try:
1263 1263 ui.write(r.revision(r.lookup(rev)))
1264 1264 except KeyError:
1265 1265 raise util.Abort(_('invalid revision identifier %s') % rev)
1266 1266
1267 1267 def debugindex(ui, file_):
1268 1268 """dump the contents of an index file"""
1269 1269 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1270 1270 ui.write(" rev offset length base linkrev" +
1271 1271 " nodeid p1 p2\n")
1272 1272 for i in xrange(r.count()):
1273 1273 node = r.node(i)
1274 1274 pp = r.parents(node)
1275 1275 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1276 1276 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1277 1277 short(node), short(pp[0]), short(pp[1])))
1278 1278
1279 1279 def debugindexdot(ui, file_):
1280 1280 """dump an index DAG as a .dot file"""
1281 1281 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1282 1282 ui.write("digraph G {\n")
1283 1283 for i in xrange(r.count()):
1284 1284 node = r.node(i)
1285 1285 pp = r.parents(node)
1286 1286 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1287 1287 if pp[1] != nullid:
1288 1288 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1289 1289 ui.write("}\n")
1290 1290
1291 1291 def debugrename(ui, repo, file, rev=None):
1292 1292 """dump rename information"""
1293 1293 r = repo.file(relpath(repo, [file])[0])
1294 1294 if rev:
1295 1295 try:
1296 1296 # assume all revision numbers are for changesets
1297 1297 n = repo.lookup(rev)
1298 1298 change = repo.changelog.read(n)
1299 1299 m = repo.manifest.read(change[0])
1300 1300 n = m[relpath(repo, [file])[0]]
1301 1301 except (hg.RepoError, KeyError):
1302 1302 n = r.lookup(rev)
1303 1303 else:
1304 1304 n = r.tip()
1305 1305 m = r.renamed(n)
1306 1306 if m:
1307 1307 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1308 1308 else:
1309 1309 ui.write(_("not renamed\n"))
1310 1310
1311 1311 def debugwalk(ui, repo, *pats, **opts):
1312 1312 """show how files match on given patterns"""
1313 1313 items = list(cmdutil.walk(repo, pats, opts))
1314 1314 if not items:
1315 1315 return
1316 1316 fmt = '%%s %%-%ds %%-%ds %%s' % (
1317 1317 max([len(abs) for (src, abs, rel, exact) in items]),
1318 1318 max([len(rel) for (src, abs, rel, exact) in items]))
1319 1319 for src, abs, rel, exact in items:
1320 1320 line = fmt % (src, abs, rel, exact and 'exact' or '')
1321 1321 ui.write("%s\n" % line.rstrip())
1322 1322
1323 1323 def diff(ui, repo, *pats, **opts):
1324 1324 """diff repository (or selected files)
1325 1325
1326 1326 Show differences between revisions for the specified files.
1327 1327
1328 1328 Differences between files are shown using the unified diff format.
1329 1329
1330 1330 When two revision arguments are given, then changes are shown
1331 1331 between those revisions. If only one revision is specified then
1332 1332 that revision is compared to the working directory, and, when no
1333 1333 revisions are specified, the working directory files are compared
1334 1334 to its parent.
1335 1335
1336 1336 Without the -a option, diff will avoid generating diffs of files
1337 1337 it detects as binary. With -a, diff will generate a diff anyway,
1338 1338 probably with undesirable results.
1339 1339 """
1340 1340 node1, node2 = cmdutil.revpair(ui, repo, opts['rev'])
1341 1341
1342 1342 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1343 1343
1344 1344 patch.diff(repo, node1, node2, fns, match=matchfn,
1345 1345 opts=patch.diffopts(ui, opts))
1346 1346
1347 1347 def export(ui, repo, *changesets, **opts):
1348 1348 """dump the header and diffs for one or more changesets
1349 1349
1350 1350 Print the changeset header and diffs for one or more revisions.
1351 1351
1352 1352 The information shown in the changeset header is: author,
1353 1353 changeset hash, parent and commit comment.
1354 1354
1355 1355 Output may be to a file, in which case the name of the file is
1356 1356 given using a format string. The formatting rules are as follows:
1357 1357
1358 1358 %% literal "%" character
1359 1359 %H changeset hash (40 bytes of hexadecimal)
1360 1360 %N number of patches being generated
1361 1361 %R changeset revision number
1362 1362 %b basename of the exporting repository
1363 1363 %h short-form changeset hash (12 bytes of hexadecimal)
1364 1364 %n zero-padded sequence number, starting at 1
1365 1365 %r zero-padded changeset revision number
1366 1366
1367 1367 Without the -a option, export will avoid generating diffs of files
1368 1368 it detects as binary. With -a, export will generate a diff anyway,
1369 1369 probably with undesirable results.
1370 1370
1371 1371 With the --switch-parent option, the diff will be against the second
1372 1372 parent. It can be useful to review a merge.
1373 1373 """
1374 1374 if not changesets:
1375 1375 raise util.Abort(_("export requires at least one changeset"))
1376 1376 revs = cmdutil.revrange(ui, repo, changesets)
1377 1377 if len(revs) > 1:
1378 1378 ui.note(_('exporting patches:\n'))
1379 1379 else:
1380 1380 ui.note(_('exporting patch:\n'))
1381 1381 patch.export(repo, map(repo.lookup, revs), template=opts['output'],
1382 1382 switch_parent=opts['switch_parent'],
1383 1383 opts=patch.diffopts(ui, opts))
1384 1384
1385 1385 def grep(ui, repo, pattern, *pats, **opts):
1386 1386 """search for a pattern in specified files and revisions
1387 1387
1388 1388 Search revisions of files for a regular expression.
1389 1389
1390 1390 This command behaves differently than Unix grep. It only accepts
1391 1391 Python/Perl regexps. It searches repository history, not the
1392 1392 working directory. It always prints the revision number in which
1393 1393 a match appears.
1394 1394
1395 1395 By default, grep only prints output for the first revision of a
1396 1396 file in which it finds a match. To get it to print every revision
1397 1397 that contains a change in match status ("-" for a match that
1398 1398 becomes a non-match, or "+" for a non-match that becomes a match),
1399 1399 use the --all flag.
1400 1400 """
1401 1401 reflags = 0
1402 1402 if opts['ignore_case']:
1403 1403 reflags |= re.I
1404 1404 regexp = re.compile(pattern, reflags)
1405 1405 sep, eol = ':', '\n'
1406 1406 if opts['print0']:
1407 1407 sep = eol = '\0'
1408 1408
1409 1409 fcache = {}
1410 1410 def getfile(fn):
1411 1411 if fn not in fcache:
1412 1412 fcache[fn] = repo.file(fn)
1413 1413 return fcache[fn]
1414 1414
1415 1415 def matchlines(body):
1416 1416 begin = 0
1417 1417 linenum = 0
1418 1418 while True:
1419 1419 match = regexp.search(body, begin)
1420 1420 if not match:
1421 1421 break
1422 1422 mstart, mend = match.span()
1423 1423 linenum += body.count('\n', begin, mstart) + 1
1424 1424 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1425 1425 lend = body.find('\n', mend)
1426 1426 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1427 1427 begin = lend + 1
1428 1428
1429 1429 class linestate(object):
1430 1430 def __init__(self, line, linenum, colstart, colend):
1431 1431 self.line = line
1432 1432 self.linenum = linenum
1433 1433 self.colstart = colstart
1434 1434 self.colend = colend
1435 1435
1436 1436 def __eq__(self, other):
1437 1437 return self.line == other.line
1438 1438
1439 1439 matches = {}
1440 1440 copies = {}
1441 1441 def grepbody(fn, rev, body):
1442 1442 matches[rev].setdefault(fn, [])
1443 1443 m = matches[rev][fn]
1444 1444 for lnum, cstart, cend, line in matchlines(body):
1445 1445 s = linestate(line, lnum, cstart, cend)
1446 1446 m.append(s)
1447 1447
1448 1448 def difflinestates(a, b):
1449 1449 sm = difflib.SequenceMatcher(None, a, b)
1450 1450 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1451 1451 if tag == 'insert':
1452 1452 for i in xrange(blo, bhi):
1453 1453 yield ('+', b[i])
1454 1454 elif tag == 'delete':
1455 1455 for i in xrange(alo, ahi):
1456 1456 yield ('-', a[i])
1457 1457 elif tag == 'replace':
1458 1458 for i in xrange(alo, ahi):
1459 1459 yield ('-', a[i])
1460 1460 for i in xrange(blo, bhi):
1461 1461 yield ('+', b[i])
1462 1462
1463 1463 prev = {}
1464 1464 def display(fn, rev, states, prevstates):
1465 1465 counts = {'-': 0, '+': 0}
1466 1466 filerevmatches = {}
1467 1467 if incrementing or not opts['all']:
1468 1468 a, b, r = prevstates, states, rev
1469 1469 else:
1470 1470 a, b, r = states, prevstates, prev.get(fn, -1)
1471 1471 for change, l in difflinestates(a, b):
1472 1472 cols = [fn, str(r)]
1473 1473 if opts['line_number']:
1474 1474 cols.append(str(l.linenum))
1475 1475 if opts['all']:
1476 1476 cols.append(change)
1477 1477 if opts['user']:
1478 1478 cols.append(ui.shortuser(getchange(r)[1]))
1479 1479 if opts['files_with_matches']:
1480 1480 c = (fn, r)
1481 1481 if c in filerevmatches:
1482 1482 continue
1483 1483 filerevmatches[c] = 1
1484 1484 else:
1485 1485 cols.append(l.line)
1486 1486 ui.write(sep.join(cols), eol)
1487 1487 counts[change] += 1
1488 1488 return counts['+'], counts['-']
1489 1489
1490 1490 fstate = {}
1491 1491 skip = {}
1492 1492 getchange = util.cachefunc(lambda r:repo.changectx(r).changeset())
1493 1493 changeiter, matchfn = walkchangerevs(ui, repo, pats, getchange, opts)
1494 1494 count = 0
1495 1495 incrementing = False
1496 1496 follow = opts.get('follow')
1497 1497 for st, rev, fns in changeiter:
1498 1498 if st == 'window':
1499 1499 incrementing = rev
1500 1500 matches.clear()
1501 1501 elif st == 'add':
1502 1502 mf = repo.changectx(rev).manifest()
1503 1503 matches[rev] = {}
1504 1504 for fn in fns:
1505 1505 if fn in skip:
1506 1506 continue
1507 1507 fstate.setdefault(fn, {})
1508 1508 try:
1509 1509 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1510 1510 if follow:
1511 1511 copied = getfile(fn).renamed(mf[fn])
1512 1512 if copied:
1513 1513 copies.setdefault(rev, {})[fn] = copied[0]
1514 1514 except KeyError:
1515 1515 pass
1516 1516 elif st == 'iter':
1517 1517 states = matches[rev].items()
1518 1518 states.sort()
1519 1519 for fn, m in states:
1520 1520 copy = copies.get(rev, {}).get(fn)
1521 1521 if fn in skip:
1522 1522 if copy:
1523 1523 skip[copy] = True
1524 1524 continue
1525 1525 if incrementing or not opts['all'] or fstate[fn]:
1526 1526 pos, neg = display(fn, rev, m, fstate[fn])
1527 1527 count += pos + neg
1528 1528 if pos and not opts['all']:
1529 1529 skip[fn] = True
1530 1530 if copy:
1531 1531 skip[copy] = True
1532 1532 fstate[fn] = m
1533 1533 if copy:
1534 1534 fstate[copy] = m
1535 1535 prev[fn] = rev
1536 1536
1537 1537 if not incrementing:
1538 1538 fstate = fstate.items()
1539 1539 fstate.sort()
1540 1540 for fn, state in fstate:
1541 1541 if fn in skip:
1542 1542 continue
1543 1543 if fn not in copies.get(prev[fn], {}):
1544 1544 display(fn, rev, {}, state)
1545 1545 return (count == 0 and 1) or 0
1546 1546
1547 1547 def heads(ui, repo, **opts):
1548 1548 """show current repository heads
1549 1549
1550 1550 Show all repository head changesets.
1551 1551
1552 1552 Repository "heads" are changesets that don't have children
1553 1553 changesets. They are where development generally takes place and
1554 1554 are the usual targets for update and merge operations.
1555 1555 """
1556 1556 if opts['rev']:
1557 1557 heads = repo.heads(repo.lookup(opts['rev']))
1558 1558 else:
1559 1559 heads = repo.heads()
1560 1560 br = None
1561 1561 if opts['branches']:
1562 1562 ui.warn(_("the --branches option is deprecated, "
1563 1563 "please use 'hg branches' instead\n"))
1564 1564 br = repo.branchlookup(heads)
1565 1565 displayer = show_changeset(ui, repo, opts)
1566 1566 for n in heads:
1567 1567 displayer.show(changenode=n, brinfo=br)
1568 1568
1569 1569 def identify(ui, repo):
1570 1570 """print information about the working copy
1571 1571
1572 1572 Print a short summary of the current state of the repo.
1573 1573
1574 1574 This summary identifies the repository state using one or two parent
1575 1575 hash identifiers, followed by a "+" if there are uncommitted changes
1576 1576 in the working directory, followed by a list of tags for this revision.
1577 1577 """
1578 1578 parents = [p for p in repo.dirstate.parents() if p != nullid]
1579 1579 if not parents:
1580 1580 ui.write(_("unknown\n"))
1581 1581 return
1582 1582
1583 1583 hexfunc = ui.debugflag and hex or short
1584 1584 modified, added, removed, deleted = repo.status()[:4]
1585 1585 output = ["%s%s" %
1586 1586 ('+'.join([hexfunc(parent) for parent in parents]),
1587 1587 (modified or added or removed or deleted) and "+" or "")]
1588 1588
1589 1589 if not ui.quiet:
1590 1590
1591 1591 branch = repo.workingctx().branch()
1592 1592 if branch:
1593 1593 output.append("(%s)" % branch)
1594 1594
1595 1595 # multiple tags for a single parent separated by '/'
1596 1596 parenttags = ['/'.join(tags)
1597 1597 for tags in map(repo.nodetags, parents) if tags]
1598 1598 # tags for multiple parents separated by ' + '
1599 1599 if parenttags:
1600 1600 output.append(' + '.join(parenttags))
1601 1601
1602 1602 ui.write("%s\n" % ' '.join(output))
1603 1603
1604 1604 def import_(ui, repo, patch1, *patches, **opts):
1605 1605 """import an ordered set of patches
1606 1606
1607 1607 Import a list of patches and commit them individually.
1608 1608
1609 1609 If there are outstanding changes in the working directory, import
1610 1610 will abort unless given the -f flag.
1611 1611
1612 1612 You can import a patch straight from a mail message. Even patches
1613 1613 as attachments work (body part must be type text/plain or
1614 1614 text/x-patch to be used). From and Subject headers of email
1615 1615 message are used as default committer and commit message. All
1616 1616 text/plain body parts before first diff are added to commit
1617 1617 message.
1618 1618
1619 1619 If imported patch was generated by hg export, user and description
1620 1620 from patch override values from message headers and body. Values
1621 1621 given on command line with -m and -u override these.
1622 1622
1623 1623 To read a patch from standard input, use patch name "-".
1624 1624 """
1625 1625 patches = (patch1,) + patches
1626 1626
1627 1627 if not opts['force']:
1628 1628 bail_if_changed(repo)
1629 1629
1630 1630 d = opts["base"]
1631 1631 strip = opts["strip"]
1632 1632
1633 1633 wlock = repo.wlock()
1634 1634 lock = repo.lock()
1635 1635
1636 1636 for p in patches:
1637 1637 pf = os.path.join(d, p)
1638 1638
1639 1639 if pf == '-':
1640 1640 ui.status(_("applying patch from stdin\n"))
1641 1641 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1642 1642 else:
1643 1643 ui.status(_("applying %s\n") % p)
1644 1644 tmpname, message, user, date = patch.extract(ui, file(pf))
1645 1645
1646 1646 if tmpname is None:
1647 1647 raise util.Abort(_('no diffs found'))
1648 1648
1649 1649 try:
1650 1650 if opts['message']:
1651 1651 # pickup the cmdline msg
1652 1652 message = opts['message']
1653 1653 elif message:
1654 1654 # pickup the patch msg
1655 1655 message = message.strip()
1656 1656 else:
1657 1657 # launch the editor
1658 1658 message = None
1659 1659 ui.debug(_('message:\n%s\n') % message)
1660 1660
1661 1661 files = {}
1662 1662 try:
1663 1663 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1664 1664 files=files)
1665 1665 finally:
1666 1666 files = patch.updatedir(ui, repo, files, wlock=wlock)
1667 1667 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1668 1668 finally:
1669 1669 os.unlink(tmpname)
1670 1670
1671 1671 def incoming(ui, repo, source="default", **opts):
1672 1672 """show new changesets found in source
1673 1673
1674 1674 Show new changesets found in the specified path/URL or the default
1675 1675 pull location. These are the changesets that would be pulled if a pull
1676 1676 was requested.
1677 1677
1678 1678 For remote repository, using --bundle avoids downloading the changesets
1679 1679 twice if the incoming is followed by a pull.
1680 1680
1681 1681 See pull for valid source format details.
1682 1682 """
1683 1683 source = ui.expandpath(source)
1684 1684 setremoteconfig(ui, opts)
1685 1685
1686 1686 other = hg.repository(ui, source)
1687 1687 incoming = repo.findincoming(other, force=opts["force"])
1688 1688 if not incoming:
1689 1689 ui.status(_("no changes found\n"))
1690 1690 return
1691 1691
1692 1692 cleanup = None
1693 1693 try:
1694 1694 fname = opts["bundle"]
1695 1695 if fname or not other.local():
1696 1696 # create a bundle (uncompressed if other repo is not local)
1697 1697 cg = other.changegroup(incoming, "incoming")
1698 1698 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1699 1699 # keep written bundle?
1700 1700 if opts["bundle"]:
1701 1701 cleanup = None
1702 1702 if not other.local():
1703 1703 # use the created uncompressed bundlerepo
1704 1704 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1705 1705
1706 1706 revs = None
1707 1707 if opts['rev']:
1708 1708 revs = [other.lookup(rev) for rev in opts['rev']]
1709 1709 o = other.changelog.nodesbetween(incoming, revs)[0]
1710 1710 if opts['newest_first']:
1711 1711 o.reverse()
1712 1712 displayer = show_changeset(ui, other, opts)
1713 1713 for n in o:
1714 1714 parents = [p for p in other.changelog.parents(n) if p != nullid]
1715 1715 if opts['no_merges'] and len(parents) == 2:
1716 1716 continue
1717 1717 displayer.show(changenode=n)
1718 1718 if opts['patch']:
1719 1719 prev = (parents and parents[0]) or nullid
1720 1720 patch.diff(other, prev, n, fp=repo.ui)
1721 1721 ui.write("\n")
1722 1722 finally:
1723 1723 if hasattr(other, 'close'):
1724 1724 other.close()
1725 1725 if cleanup:
1726 1726 os.unlink(cleanup)
1727 1727
1728 1728 def init(ui, dest=".", **opts):
1729 1729 """create a new repository in the given directory
1730 1730
1731 1731 Initialize a new repository in the given directory. If the given
1732 1732 directory does not exist, it is created.
1733 1733
1734 1734 If no directory is given, the current directory is used.
1735 1735
1736 1736 It is possible to specify an ssh:// URL as the destination.
1737 1737 Look at the help text for the pull command for important details
1738 1738 about ssh:// URLs.
1739 1739 """
1740 1740 setremoteconfig(ui, opts)
1741 1741 hg.repository(ui, dest, create=1)
1742 1742
1743 1743 def locate(ui, repo, *pats, **opts):
1744 1744 """locate files matching specific patterns
1745 1745
1746 1746 Print all files under Mercurial control whose names match the
1747 1747 given patterns.
1748 1748
1749 1749 This command searches the current directory and its
1750 1750 subdirectories. To search an entire repository, move to the root
1751 1751 of the repository.
1752 1752
1753 1753 If no patterns are given to match, this command prints all file
1754 1754 names.
1755 1755
1756 1756 If you want to feed the output of this command into the "xargs"
1757 1757 command, use the "-0" option to both this command and "xargs".
1758 1758 This will avoid the problem of "xargs" treating single filenames
1759 1759 that contain white space as multiple filenames.
1760 1760 """
1761 1761 end = opts['print0'] and '\0' or '\n'
1762 1762 rev = opts['rev']
1763 1763 if rev:
1764 1764 node = repo.lookup(rev)
1765 1765 else:
1766 1766 node = None
1767 1767
1768 1768 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1769 1769 head='(?:.*/|)'):
1770 1770 if not node and repo.dirstate.state(abs) == '?':
1771 1771 continue
1772 1772 if opts['fullpath']:
1773 1773 ui.write(os.path.join(repo.root, abs), end)
1774 1774 else:
1775 1775 ui.write(((pats and rel) or abs), end)
1776 1776
1777 1777 def log(ui, repo, *pats, **opts):
1778 1778 """show revision history of entire repository or files
1779 1779
1780 1780 Print the revision history of the specified files or the entire
1781 1781 project.
1782 1782
1783 1783 File history is shown without following rename or copy history of
1784 1784 files. Use -f/--follow with a file name to follow history across
1785 1785 renames and copies. --follow without a file name will only show
1786 1786 ancestors or descendants of the starting revision. --follow-first
1787 1787 only follows the first parent of merge revisions.
1788 1788
1789 1789 If no revision range is specified, the default is tip:0 unless
1790 1790 --follow is set, in which case the working directory parent is
1791 1791 used as the starting revision.
1792 1792
1793 1793 By default this command outputs: changeset id and hash, tags,
1794 1794 non-trivial parents, user, date and time, and a summary for each
1795 1795 commit. When the -v/--verbose switch is used, the list of changed
1796 1796 files and full commit message is shown.
1797 1797 """
1798 1798 class dui(object):
1799 1799 # Implement and delegate some ui protocol. Save hunks of
1800 1800 # output for later display in the desired order.
1801 1801 def __init__(self, ui):
1802 1802 self.ui = ui
1803 1803 self.hunk = {}
1804 1804 self.header = {}
1805 self.quiet = ui.quiet
1806 self.verbose = ui.verbose
1807 self.debugflag = ui.debugflag
1805 1808 def bump(self, rev):
1806 1809 self.rev = rev
1807 1810 self.hunk[rev] = []
1808 1811 self.header[rev] = []
1809 1812 def note(self, *args):
1810 1813 if self.verbose:
1811 1814 self.write(*args)
1812 1815 def status(self, *args):
1813 1816 if not self.quiet:
1814 1817 self.write(*args)
1815 1818 def write(self, *args):
1816 1819 self.hunk[self.rev].append(args)
1817 1820 def write_header(self, *args):
1818 1821 self.header[self.rev].append(args)
1819 1822 def debug(self, *args):
1820 1823 if self.debugflag:
1821 1824 self.write(*args)
1822 1825 def __getattr__(self, key):
1823 1826 return getattr(self.ui, key)
1824 1827
1825 1828 getchange = util.cachefunc(lambda r:repo.changectx(r).changeset())
1826 1829 changeiter, matchfn = walkchangerevs(ui, repo, pats, getchange, opts)
1827 1830
1828 1831 if opts['branches']:
1829 1832 ui.warn(_("the --branches option is deprecated, "
1830 1833 "please use 'hg branches' instead\n"))
1831 1834
1832 1835 if opts['limit']:
1833 1836 try:
1834 1837 limit = int(opts['limit'])
1835 1838 except ValueError:
1836 1839 raise util.Abort(_('limit must be a positive integer'))
1837 1840 if limit <= 0: raise util.Abort(_('limit must be positive'))
1838 1841 else:
1839 1842 limit = sys.maxint
1840 1843 count = 0
1841 1844
1842 1845 if opts['copies'] and opts['rev']:
1843 1846 endrev = max(cmdutil.revrange(ui, repo, opts['rev'])) + 1
1844 1847 else:
1845 1848 endrev = repo.changelog.count()
1846 1849 rcache = {}
1847 1850 ncache = {}
1848 1851 dcache = []
1849 1852 def getrenamed(fn, rev, man):
1850 1853 '''looks up all renames for a file (up to endrev) the first
1851 1854 time the file is given. It indexes on the changerev and only
1852 1855 parses the manifest if linkrev != changerev.
1853 1856 Returns rename info for fn at changerev rev.'''
1854 1857 if fn not in rcache:
1855 1858 rcache[fn] = {}
1856 1859 ncache[fn] = {}
1857 1860 fl = repo.file(fn)
1858 1861 for i in xrange(fl.count()):
1859 1862 node = fl.node(i)
1860 1863 lr = fl.linkrev(node)
1861 1864 renamed = fl.renamed(node)
1862 1865 rcache[fn][lr] = renamed
1863 1866 if renamed:
1864 1867 ncache[fn][node] = renamed
1865 1868 if lr >= endrev:
1866 1869 break
1867 1870 if rev in rcache[fn]:
1868 1871 return rcache[fn][rev]
1869 1872 mr = repo.manifest.rev(man)
1870 1873 if repo.manifest.parentrevs(mr) != (mr - 1, -1):
1871 1874 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1872 1875 if not dcache or dcache[0] != man:
1873 1876 dcache[:] = [man, repo.manifest.readdelta(man)]
1874 1877 if fn in dcache[1]:
1875 1878 return ncache[fn].get(dcache[1][fn])
1876 1879 return None
1877 1880
1878 1881 displayer = show_changeset(ui, repo, opts)
1879 1882 for st, rev, fns in changeiter:
1880 1883 if st == 'window':
1881 1884 du = dui(ui)
1882 1885 displayer.ui = du
1883 1886 elif st == 'add':
1884 1887 du.bump(rev)
1885 1888 changenode = repo.changelog.node(rev)
1886 1889 parents = [p for p in repo.changelog.parents(changenode)
1887 1890 if p != nullid]
1888 1891 if opts['no_merges'] and len(parents) == 2:
1889 1892 continue
1890 1893 if opts['only_merges'] and len(parents) != 2:
1891 1894 continue
1892 1895
1893 1896 if opts['keyword']:
1894 1897 changes = getchange(rev)
1895 1898 miss = 0
1896 1899 for k in [kw.lower() for kw in opts['keyword']]:
1897 1900 if not (k in changes[1].lower() or
1898 1901 k in changes[4].lower() or
1899 1902 k in " ".join(changes[3][:20]).lower()):
1900 1903 miss = 1
1901 1904 break
1902 1905 if miss:
1903 1906 continue
1904 1907
1905 1908 br = None
1906 1909 if opts['branches']:
1907 1910 br = repo.branchlookup([repo.changelog.node(rev)])
1908 1911
1909 1912 copies = []
1910 1913 if opts.get('copies') and rev:
1911 1914 mf = getchange(rev)[0]
1912 1915 for fn in getchange(rev)[3]:
1913 1916 rename = getrenamed(fn, rev, mf)
1914 1917 if rename:
1915 1918 copies.append((fn, rename[0]))
1916 1919 displayer.show(rev, brinfo=br, copies=copies)
1917 1920 if opts['patch']:
1918 1921 prev = (parents and parents[0]) or nullid
1919 1922 patch.diff(repo, prev, changenode, match=matchfn, fp=du)
1920 1923 du.write("\n\n")
1921 1924 elif st == 'iter':
1922 1925 if count == limit: break
1923 1926 if du.header[rev]:
1924 1927 for args in du.header[rev]:
1925 1928 ui.write_header(*args)
1926 1929 if du.hunk[rev]:
1927 1930 count += 1
1928 1931 for args in du.hunk[rev]:
1929 1932 ui.write(*args)
1930 1933
1931 1934 def manifest(ui, repo, rev=None):
1932 1935 """output the latest or given revision of the project manifest
1933 1936
1934 1937 Print a list of version controlled files for the given revision.
1935 1938
1936 1939 The manifest is the list of files being version controlled. If no revision
1937 1940 is given then the tip is used.
1938 1941 """
1939 1942 if rev:
1940 1943 try:
1941 1944 # assume all revision numbers are for changesets
1942 1945 n = repo.lookup(rev)
1943 1946 change = repo.changelog.read(n)
1944 1947 n = change[0]
1945 1948 except hg.RepoError:
1946 1949 n = repo.manifest.lookup(rev)
1947 1950 else:
1948 1951 n = repo.manifest.tip()
1949 1952 m = repo.manifest.read(n)
1950 1953 files = m.keys()
1951 1954 files.sort()
1952 1955
1953 1956 for f in files:
1954 1957 ui.write("%40s %3s %s\n" % (hex(m[f]),
1955 1958 m.execf(f) and "755" or "644", f))
1956 1959
1957 1960 def merge(ui, repo, node=None, force=None, branch=None):
1958 1961 """Merge working directory with another revision
1959 1962
1960 1963 Merge the contents of the current working directory and the
1961 1964 requested revision. Files that changed between either parent are
1962 1965 marked as changed for the next commit and a commit must be
1963 1966 performed before any further updates are allowed.
1964 1967
1965 1968 If no revision is specified, the working directory's parent is a
1966 1969 head revision, and the repository contains exactly one other head,
1967 1970 the other head is merged with by default. Otherwise, an explicit
1968 1971 revision to merge with must be provided.
1969 1972 """
1970 1973
1971 1974 if node or branch:
1972 1975 node = _lookup(repo, node, branch)
1973 1976 else:
1974 1977 heads = repo.heads()
1975 1978 if len(heads) > 2:
1976 1979 raise util.Abort(_('repo has %d heads - '
1977 1980 'please merge with an explicit rev') %
1978 1981 len(heads))
1979 1982 if len(heads) == 1:
1980 1983 raise util.Abort(_('there is nothing to merge - '
1981 1984 'use "hg update" instead'))
1982 1985 parent = repo.dirstate.parents()[0]
1983 1986 if parent not in heads:
1984 1987 raise util.Abort(_('working dir not at a head rev - '
1985 1988 'use "hg update" or merge with an explicit rev'))
1986 1989 node = parent == heads[0] and heads[-1] or heads[0]
1987 1990 return hg.merge(repo, node, force=force)
1988 1991
1989 1992 def outgoing(ui, repo, dest=None, **opts):
1990 1993 """show changesets not found in destination
1991 1994
1992 1995 Show changesets not found in the specified destination repository or
1993 1996 the default push location. These are the changesets that would be pushed
1994 1997 if a push was requested.
1995 1998
1996 1999 See pull for valid destination format details.
1997 2000 """
1998 2001 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1999 2002 setremoteconfig(ui, opts)
2000 2003 revs = None
2001 2004 if opts['rev']:
2002 2005 revs = [repo.lookup(rev) for rev in opts['rev']]
2003 2006
2004 2007 other = hg.repository(ui, dest)
2005 2008 o = repo.findoutgoing(other, force=opts['force'])
2006 2009 if not o:
2007 2010 ui.status(_("no changes found\n"))
2008 2011 return
2009 2012 o = repo.changelog.nodesbetween(o, revs)[0]
2010 2013 if opts['newest_first']:
2011 2014 o.reverse()
2012 2015 displayer = show_changeset(ui, repo, opts)
2013 2016 for n in o:
2014 2017 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2015 2018 if opts['no_merges'] and len(parents) == 2:
2016 2019 continue
2017 2020 displayer.show(changenode=n)
2018 2021 if opts['patch']:
2019 2022 prev = (parents and parents[0]) or nullid
2020 2023 patch.diff(repo, prev, n)
2021 2024 ui.write("\n")
2022 2025
2023 2026 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
2024 2027 """show the parents of the working dir or revision
2025 2028
2026 2029 Print the working directory's parent revisions.
2027 2030 """
2028 2031 # legacy
2029 2032 if file_ and not rev:
2030 2033 try:
2031 2034 rev = repo.lookup(file_)
2032 2035 file_ = None
2033 2036 except hg.RepoError:
2034 2037 pass
2035 2038 else:
2036 2039 ui.warn(_("'hg parent REV' is deprecated, "
2037 2040 "please use 'hg parents -r REV instead\n"))
2038 2041
2039 2042 if rev:
2040 2043 if file_:
2041 2044 ctx = repo.filectx(file_, changeid=rev)
2042 2045 else:
2043 2046 ctx = repo.changectx(rev)
2044 2047 p = [cp.node() for cp in ctx.parents()]
2045 2048 else:
2046 2049 p = repo.dirstate.parents()
2047 2050
2048 2051 br = None
2049 2052 if branches is not None:
2050 2053 ui.warn(_("the --branches option is deprecated, "
2051 2054 "please use 'hg branches' instead\n"))
2052 2055 br = repo.branchlookup(p)
2053 2056 displayer = show_changeset(ui, repo, opts)
2054 2057 for n in p:
2055 2058 if n != nullid:
2056 2059 displayer.show(changenode=n, brinfo=br)
2057 2060
2058 2061 def paths(ui, repo, search=None):
2059 2062 """show definition of symbolic path names
2060 2063
2061 2064 Show definition of symbolic path name NAME. If no name is given, show
2062 2065 definition of available names.
2063 2066
2064 2067 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2065 2068 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2066 2069 """
2067 2070 if search:
2068 2071 for name, path in ui.configitems("paths"):
2069 2072 if name == search:
2070 2073 ui.write("%s\n" % path)
2071 2074 return
2072 2075 ui.warn(_("not found!\n"))
2073 2076 return 1
2074 2077 else:
2075 2078 for name, path in ui.configitems("paths"):
2076 2079 ui.write("%s = %s\n" % (name, path))
2077 2080
2078 2081 def postincoming(ui, repo, modheads, optupdate):
2079 2082 if modheads == 0:
2080 2083 return
2081 2084 if optupdate:
2082 2085 if modheads == 1:
2083 2086 return hg.update(repo, repo.changelog.tip()) # update
2084 2087 else:
2085 2088 ui.status(_("not updating, since new heads added\n"))
2086 2089 if modheads > 1:
2087 2090 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2088 2091 else:
2089 2092 ui.status(_("(run 'hg update' to get a working copy)\n"))
2090 2093
2091 2094 def pull(ui, repo, source="default", **opts):
2092 2095 """pull changes from the specified source
2093 2096
2094 2097 Pull changes from a remote repository to a local one.
2095 2098
2096 2099 This finds all changes from the repository at the specified path
2097 2100 or URL and adds them to the local repository. By default, this
2098 2101 does not update the copy of the project in the working directory.
2099 2102
2100 2103 Valid URLs are of the form:
2101 2104
2102 2105 local/filesystem/path (or file://local/filesystem/path)
2103 2106 http://[user@]host[:port]/[path]
2104 2107 https://[user@]host[:port]/[path]
2105 2108 ssh://[user@]host[:port]/[path]
2106 2109 static-http://host[:port]/[path]
2107 2110
2108 2111 Paths in the local filesystem can either point to Mercurial
2109 2112 repositories or to bundle files (as created by 'hg bundle' or
2110 2113 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2111 2114 allows access to a Mercurial repository where you simply use a web
2112 2115 server to publish the .hg directory as static content.
2113 2116
2114 2117 Some notes about using SSH with Mercurial:
2115 2118 - SSH requires an accessible shell account on the destination machine
2116 2119 and a copy of hg in the remote path or specified with as remotecmd.
2117 2120 - path is relative to the remote user's home directory by default.
2118 2121 Use an extra slash at the start of a path to specify an absolute path:
2119 2122 ssh://example.com//tmp/repository
2120 2123 - Mercurial doesn't use its own compression via SSH; the right thing
2121 2124 to do is to configure it in your ~/.ssh/config, e.g.:
2122 2125 Host *.mylocalnetwork.example.com
2123 2126 Compression no
2124 2127 Host *
2125 2128 Compression yes
2126 2129 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2127 2130 with the --ssh command line option.
2128 2131 """
2129 2132 source = ui.expandpath(source)
2130 2133 setremoteconfig(ui, opts)
2131 2134
2132 2135 other = hg.repository(ui, source)
2133 2136 ui.status(_('pulling from %s\n') % (source))
2134 2137 revs = None
2135 2138 if opts['rev']:
2136 2139 if 'lookup' in other.capabilities:
2137 2140 revs = [other.lookup(rev) for rev in opts['rev']]
2138 2141 else:
2139 2142 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
2140 2143 raise util.Abort(error)
2141 2144 modheads = repo.pull(other, heads=revs, force=opts['force'])
2142 2145 return postincoming(ui, repo, modheads, opts['update'])
2143 2146
2144 2147 def push(ui, repo, dest=None, **opts):
2145 2148 """push changes to the specified destination
2146 2149
2147 2150 Push changes from the local repository to the given destination.
2148 2151
2149 2152 This is the symmetrical operation for pull. It helps to move
2150 2153 changes from the current repository to a different one. If the
2151 2154 destination is local this is identical to a pull in that directory
2152 2155 from the current one.
2153 2156
2154 2157 By default, push will refuse to run if it detects the result would
2155 2158 increase the number of remote heads. This generally indicates the
2156 2159 the client has forgotten to sync and merge before pushing.
2157 2160
2158 2161 Valid URLs are of the form:
2159 2162
2160 2163 local/filesystem/path (or file://local/filesystem/path)
2161 2164 ssh://[user@]host[:port]/[path]
2162 2165 http://[user@]host[:port]/[path]
2163 2166 https://[user@]host[:port]/[path]
2164 2167
2165 2168 Look at the help text for the pull command for important details
2166 2169 about ssh:// URLs.
2167 2170
2168 2171 Pushing to http:// and https:// URLs is only possible, if this
2169 2172 feature is explicitly enabled on the remote Mercurial server.
2170 2173 """
2171 2174 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2172 2175 setremoteconfig(ui, opts)
2173 2176
2174 2177 other = hg.repository(ui, dest)
2175 2178 ui.status('pushing to %s\n' % (dest))
2176 2179 revs = None
2177 2180 if opts['rev']:
2178 2181 revs = [repo.lookup(rev) for rev in opts['rev']]
2179 2182 r = repo.push(other, opts['force'], revs=revs)
2180 2183 return r == 0
2181 2184
2182 2185 def rawcommit(ui, repo, *flist, **rc):
2183 2186 """raw commit interface (DEPRECATED)
2184 2187
2185 2188 (DEPRECATED)
2186 2189 Lowlevel commit, for use in helper scripts.
2187 2190
2188 2191 This command is not intended to be used by normal users, as it is
2189 2192 primarily useful for importing from other SCMs.
2190 2193
2191 2194 This command is now deprecated and will be removed in a future
2192 2195 release, please use debugsetparents and commit instead.
2193 2196 """
2194 2197
2195 2198 ui.warn(_("(the rawcommit command is deprecated)\n"))
2196 2199
2197 2200 message = rc['message']
2198 2201 if not message and rc['logfile']:
2199 2202 try:
2200 2203 message = open(rc['logfile']).read()
2201 2204 except IOError:
2202 2205 pass
2203 2206 if not message and not rc['logfile']:
2204 2207 raise util.Abort(_("missing commit message"))
2205 2208
2206 2209 files = relpath(repo, list(flist))
2207 2210 if rc['files']:
2208 2211 files += open(rc['files']).read().splitlines()
2209 2212
2210 2213 rc['parent'] = map(repo.lookup, rc['parent'])
2211 2214
2212 2215 try:
2213 2216 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2214 2217 except ValueError, inst:
2215 2218 raise util.Abort(str(inst))
2216 2219
2217 2220 def recover(ui, repo):
2218 2221 """roll back an interrupted transaction
2219 2222
2220 2223 Recover from an interrupted commit or pull.
2221 2224
2222 2225 This command tries to fix the repository status after an interrupted
2223 2226 operation. It should only be necessary when Mercurial suggests it.
2224 2227 """
2225 2228 if repo.recover():
2226 2229 return hg.verify(repo)
2227 2230 return 1
2228 2231
2229 2232 def remove(ui, repo, *pats, **opts):
2230 2233 """remove the specified files on the next commit
2231 2234
2232 2235 Schedule the indicated files for removal from the repository.
2233 2236
2234 2237 This command schedules the files to be removed at the next commit.
2235 2238 This only removes files from the current branch, not from the
2236 2239 entire project history. If the files still exist in the working
2237 2240 directory, they will be deleted from it. If invoked with --after,
2238 2241 files that have been manually deleted are marked as removed.
2239 2242
2240 2243 Modified files and added files are not removed by default. To
2241 2244 remove them, use the -f/--force option.
2242 2245 """
2243 2246 names = []
2244 2247 if not opts['after'] and not pats:
2245 2248 raise util.Abort(_('no files specified'))
2246 2249 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2247 2250 exact = dict.fromkeys(files)
2248 2251 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2249 2252 modified, added, removed, deleted, unknown = mardu
2250 2253 remove, forget = [], []
2251 2254 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2252 2255 reason = None
2253 2256 if abs not in deleted and opts['after']:
2254 2257 reason = _('is still present')
2255 2258 elif abs in modified and not opts['force']:
2256 2259 reason = _('is modified (use -f to force removal)')
2257 2260 elif abs in added:
2258 2261 if opts['force']:
2259 2262 forget.append(abs)
2260 2263 continue
2261 2264 reason = _('has been marked for add (use -f to force removal)')
2262 2265 elif abs in unknown:
2263 2266 reason = _('is not managed')
2264 2267 elif abs in removed:
2265 2268 continue
2266 2269 if reason:
2267 2270 if exact:
2268 2271 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2269 2272 else:
2270 2273 if ui.verbose or not exact:
2271 2274 ui.status(_('removing %s\n') % rel)
2272 2275 remove.append(abs)
2273 2276 repo.forget(forget)
2274 2277 repo.remove(remove, unlink=not opts['after'])
2275 2278
2276 2279 def rename(ui, repo, *pats, **opts):
2277 2280 """rename files; equivalent of copy + remove
2278 2281
2279 2282 Mark dest as copies of sources; mark sources for deletion. If
2280 2283 dest is a directory, copies are put in that directory. If dest is
2281 2284 a file, there can only be one source.
2282 2285
2283 2286 By default, this command copies the contents of files as they
2284 2287 stand in the working directory. If invoked with --after, the
2285 2288 operation is recorded, but no copying is performed.
2286 2289
2287 2290 This command takes effect in the next commit.
2288 2291
2289 2292 NOTE: This command should be treated as experimental. While it
2290 2293 should properly record rename files, this information is not yet
2291 2294 fully used by merge, nor fully reported by log.
2292 2295 """
2293 2296 wlock = repo.wlock(0)
2294 2297 errs, copied = docopy(ui, repo, pats, opts, wlock)
2295 2298 names = []
2296 2299 for abs, rel, exact in copied:
2297 2300 if ui.verbose or not exact:
2298 2301 ui.status(_('removing %s\n') % rel)
2299 2302 names.append(abs)
2300 2303 if not opts.get('dry_run'):
2301 2304 repo.remove(names, True, wlock)
2302 2305 return errs
2303 2306
2304 2307 def revert(ui, repo, *pats, **opts):
2305 2308 """revert files or dirs to their states as of some revision
2306 2309
2307 2310 With no revision specified, revert the named files or directories
2308 2311 to the contents they had in the parent of the working directory.
2309 2312 This restores the contents of the affected files to an unmodified
2310 2313 state. If the working directory has two parents, you must
2311 2314 explicitly specify the revision to revert to.
2312 2315
2313 2316 Modified files are saved with a .orig suffix before reverting.
2314 2317 To disable these backups, use --no-backup.
2315 2318
2316 2319 Using the -r option, revert the given files or directories to their
2317 2320 contents as of a specific revision. This can be helpful to "roll
2318 2321 back" some or all of a change that should not have been committed.
2319 2322
2320 2323 Revert modifies the working directory. It does not commit any
2321 2324 changes, or change the parent of the working directory. If you
2322 2325 revert to a revision other than the parent of the working
2323 2326 directory, the reverted files will thus appear modified
2324 2327 afterwards.
2325 2328
2326 2329 If a file has been deleted, it is recreated. If the executable
2327 2330 mode of a file was changed, it is reset.
2328 2331
2329 2332 If names are given, all files matching the names are reverted.
2330 2333
2331 2334 If no arguments are given, no files are reverted.
2332 2335 """
2333 2336
2334 2337 if not pats and not opts['all']:
2335 2338 raise util.Abort(_('no files or directories specified; '
2336 2339 'use --all to revert the whole repo'))
2337 2340
2338 2341 parent, p2 = repo.dirstate.parents()
2339 2342 if not opts['rev'] and p2 != nullid:
2340 2343 raise util.Abort(_('uncommitted merge - please provide a '
2341 2344 'specific revision'))
2342 2345 node = repo.changectx(opts['rev']).node()
2343 2346 mf = repo.manifest.read(repo.changelog.read(node)[0])
2344 2347 if node == parent:
2345 2348 pmf = mf
2346 2349 else:
2347 2350 pmf = None
2348 2351
2349 2352 wlock = repo.wlock()
2350 2353
2351 2354 # need all matching names in dirstate and manifest of target rev,
2352 2355 # so have to walk both. do not print errors if files exist in one
2353 2356 # but not other.
2354 2357
2355 2358 names = {}
2356 2359 target_only = {}
2357 2360
2358 2361 # walk dirstate.
2359 2362
2360 2363 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2361 2364 badmatch=mf.has_key):
2362 2365 names[abs] = (rel, exact)
2363 2366 if src == 'b':
2364 2367 target_only[abs] = True
2365 2368
2366 2369 # walk target manifest.
2367 2370
2368 2371 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2369 2372 badmatch=names.has_key):
2370 2373 if abs in names: continue
2371 2374 names[abs] = (rel, exact)
2372 2375 target_only[abs] = True
2373 2376
2374 2377 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2375 2378 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2376 2379
2377 2380 revert = ([], _('reverting %s\n'))
2378 2381 add = ([], _('adding %s\n'))
2379 2382 remove = ([], _('removing %s\n'))
2380 2383 forget = ([], _('forgetting %s\n'))
2381 2384 undelete = ([], _('undeleting %s\n'))
2382 2385 update = {}
2383 2386
2384 2387 disptable = (
2385 2388 # dispatch table:
2386 2389 # file state
2387 2390 # action if in target manifest
2388 2391 # action if not in target manifest
2389 2392 # make backup if in target manifest
2390 2393 # make backup if not in target manifest
2391 2394 (modified, revert, remove, True, True),
2392 2395 (added, revert, forget, True, False),
2393 2396 (removed, undelete, None, False, False),
2394 2397 (deleted, revert, remove, False, False),
2395 2398 (unknown, add, None, True, False),
2396 2399 (target_only, add, None, False, False),
2397 2400 )
2398 2401
2399 2402 entries = names.items()
2400 2403 entries.sort()
2401 2404
2402 2405 for abs, (rel, exact) in entries:
2403 2406 mfentry = mf.get(abs)
2404 2407 def handle(xlist, dobackup):
2405 2408 xlist[0].append(abs)
2406 2409 update[abs] = 1
2407 2410 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2408 2411 bakname = "%s.orig" % rel
2409 2412 ui.note(_('saving current version of %s as %s\n') %
2410 2413 (rel, bakname))
2411 2414 if not opts.get('dry_run'):
2412 2415 shutil.copyfile(rel, bakname)
2413 2416 shutil.copymode(rel, bakname)
2414 2417 if ui.verbose or not exact:
2415 2418 ui.status(xlist[1] % rel)
2416 2419 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2417 2420 if abs not in table: continue
2418 2421 # file has changed in dirstate
2419 2422 if mfentry:
2420 2423 handle(hitlist, backuphit)
2421 2424 elif misslist is not None:
2422 2425 handle(misslist, backupmiss)
2423 2426 else:
2424 2427 if exact: ui.warn(_('file not managed: %s\n' % rel))
2425 2428 break
2426 2429 else:
2427 2430 # file has not changed in dirstate
2428 2431 if node == parent:
2429 2432 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2430 2433 continue
2431 2434 if pmf is None:
2432 2435 # only need parent manifest in this unlikely case,
2433 2436 # so do not read by default
2434 2437 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2435 2438 if abs in pmf:
2436 2439 if mfentry:
2437 2440 # if version of file is same in parent and target
2438 2441 # manifests, do nothing
2439 2442 if pmf[abs] != mfentry:
2440 2443 handle(revert, False)
2441 2444 else:
2442 2445 handle(remove, False)
2443 2446
2444 2447 if not opts.get('dry_run'):
2445 2448 repo.dirstate.forget(forget[0])
2446 2449 r = hg.revert(repo, node, update.has_key, wlock)
2447 2450 repo.dirstate.update(add[0], 'a')
2448 2451 repo.dirstate.update(undelete[0], 'n')
2449 2452 repo.dirstate.update(remove[0], 'r')
2450 2453 return r
2451 2454
2452 2455 def rollback(ui, repo):
2453 2456 """roll back the last transaction in this repository
2454 2457
2455 2458 Roll back the last transaction in this repository, restoring the
2456 2459 project to its state prior to the transaction.
2457 2460
2458 2461 Transactions are used to encapsulate the effects of all commands
2459 2462 that create new changesets or propagate existing changesets into a
2460 2463 repository. For example, the following commands are transactional,
2461 2464 and their effects can be rolled back:
2462 2465
2463 2466 commit
2464 2467 import
2465 2468 pull
2466 2469 push (with this repository as destination)
2467 2470 unbundle
2468 2471
2469 2472 This command should be used with care. There is only one level of
2470 2473 rollback, and there is no way to undo a rollback.
2471 2474
2472 2475 This command is not intended for use on public repositories. Once
2473 2476 changes are visible for pull by other users, rolling a transaction
2474 2477 back locally is ineffective (someone else may already have pulled
2475 2478 the changes). Furthermore, a race is possible with readers of the
2476 2479 repository; for example an in-progress pull from the repository
2477 2480 may fail if a rollback is performed.
2478 2481 """
2479 2482 repo.rollback()
2480 2483
2481 2484 def root(ui, repo):
2482 2485 """print the root (top) of the current working dir
2483 2486
2484 2487 Print the root directory of the current repository.
2485 2488 """
2486 2489 ui.write(repo.root + "\n")
2487 2490
2488 2491 def serve(ui, repo, **opts):
2489 2492 """export the repository via HTTP
2490 2493
2491 2494 Start a local HTTP repository browser and pull server.
2492 2495
2493 2496 By default, the server logs accesses to stdout and errors to
2494 2497 stderr. Use the "-A" and "-E" options to log to files.
2495 2498 """
2496 2499
2497 2500 if opts["stdio"]:
2498 2501 if repo is None:
2499 2502 raise hg.RepoError(_("There is no Mercurial repository here"
2500 2503 " (.hg not found)"))
2501 2504 s = sshserver.sshserver(ui, repo)
2502 2505 s.serve_forever()
2503 2506
2504 2507 optlist = ("name templates style address port ipv6"
2505 2508 " accesslog errorlog webdir_conf")
2506 2509 for o in optlist.split():
2507 2510 if opts[o]:
2508 2511 ui.setconfig("web", o, str(opts[o]))
2509 2512
2510 2513 if repo is None and not ui.config("web", "webdir_conf"):
2511 2514 raise hg.RepoError(_("There is no Mercurial repository here"
2512 2515 " (.hg not found)"))
2513 2516
2514 2517 if opts['daemon'] and not opts['daemon_pipefds']:
2515 2518 rfd, wfd = os.pipe()
2516 2519 args = sys.argv[:]
2517 2520 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2518 2521 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2519 2522 args[0], args)
2520 2523 os.close(wfd)
2521 2524 os.read(rfd, 1)
2522 2525 os._exit(0)
2523 2526
2524 2527 try:
2525 2528 httpd = hgweb.server.create_server(ui, repo)
2526 2529 except socket.error, inst:
2527 2530 raise util.Abort(_('cannot start server: %s') % inst.args[1])
2528 2531
2529 2532 if ui.verbose:
2530 2533 addr, port = httpd.socket.getsockname()
2531 2534 if addr == '0.0.0.0':
2532 2535 addr = socket.gethostname()
2533 2536 else:
2534 2537 try:
2535 2538 addr = socket.gethostbyaddr(addr)[0]
2536 2539 except socket.error:
2537 2540 pass
2538 2541 if port != 80:
2539 2542 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2540 2543 else:
2541 2544 ui.status(_('listening at http://%s/\n') % addr)
2542 2545
2543 2546 if opts['pid_file']:
2544 2547 fp = open(opts['pid_file'], 'w')
2545 2548 fp.write(str(os.getpid()) + '\n')
2546 2549 fp.close()
2547 2550
2548 2551 if opts['daemon_pipefds']:
2549 2552 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2550 2553 os.close(rfd)
2551 2554 os.write(wfd, 'y')
2552 2555 os.close(wfd)
2553 2556 sys.stdout.flush()
2554 2557 sys.stderr.flush()
2555 2558 fd = os.open(util.nulldev, os.O_RDWR)
2556 2559 if fd != 0: os.dup2(fd, 0)
2557 2560 if fd != 1: os.dup2(fd, 1)
2558 2561 if fd != 2: os.dup2(fd, 2)
2559 2562 if fd not in (0, 1, 2): os.close(fd)
2560 2563
2561 2564 httpd.serve_forever()
2562 2565
2563 2566 def status(ui, repo, *pats, **opts):
2564 2567 """show changed files in the working directory
2565 2568
2566 2569 Show status of files in the repository. If names are given, only
2567 2570 files that match are shown. Files that are clean or ignored, are
2568 2571 not listed unless -c (clean), -i (ignored) or -A is given.
2569 2572
2570 2573 If one revision is given, it is used as the base revision.
2571 2574 If two revisions are given, the difference between them is shown.
2572 2575
2573 2576 The codes used to show the status of files are:
2574 2577 M = modified
2575 2578 A = added
2576 2579 R = removed
2577 2580 C = clean
2578 2581 ! = deleted, but still tracked
2579 2582 ? = not tracked
2580 2583 I = ignored (not shown by default)
2581 2584 = the previous added file was copied from here
2582 2585 """
2583 2586
2584 2587 all = opts['all']
2585 2588 node1, node2 = cmdutil.revpair(ui, repo, opts.get('rev'))
2586 2589
2587 2590 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2588 2591 cwd = (pats and repo.getcwd()) or ''
2589 2592 modified, added, removed, deleted, unknown, ignored, clean = [
2590 2593 [util.pathto(cwd, x) for x in n]
2591 2594 for n in repo.status(node1=node1, node2=node2, files=files,
2592 2595 match=matchfn,
2593 2596 list_ignored=all or opts['ignored'],
2594 2597 list_clean=all or opts['clean'])]
2595 2598
2596 2599 changetypes = (('modified', 'M', modified),
2597 2600 ('added', 'A', added),
2598 2601 ('removed', 'R', removed),
2599 2602 ('deleted', '!', deleted),
2600 2603 ('unknown', '?', unknown),
2601 2604 ('ignored', 'I', ignored))
2602 2605
2603 2606 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2604 2607
2605 2608 end = opts['print0'] and '\0' or '\n'
2606 2609
2607 2610 for opt, char, changes in ([ct for ct in explicit_changetypes
2608 2611 if all or opts[ct[0]]]
2609 2612 or changetypes):
2610 2613 if opts['no_status']:
2611 2614 format = "%%s%s" % end
2612 2615 else:
2613 2616 format = "%s %%s%s" % (char, end)
2614 2617
2615 2618 for f in changes:
2616 2619 ui.write(format % f)
2617 2620 if ((all or opts.get('copies')) and not opts.get('no_status')):
2618 2621 copied = repo.dirstate.copied(f)
2619 2622 if copied:
2620 2623 ui.write(' %s%s' % (copied, end))
2621 2624
2622 2625 def tag(ui, repo, name, rev_=None, **opts):
2623 2626 """add a tag for the current tip or a given revision
2624 2627
2625 2628 Name a particular revision using <name>.
2626 2629
2627 2630 Tags are used to name particular revisions of the repository and are
2628 2631 very useful to compare different revision, to go back to significant
2629 2632 earlier versions or to mark branch points as releases, etc.
2630 2633
2631 2634 If no revision is given, the parent of the working directory is used.
2632 2635
2633 2636 To facilitate version control, distribution, and merging of tags,
2634 2637 they are stored as a file named ".hgtags" which is managed
2635 2638 similarly to other project files and can be hand-edited if
2636 2639 necessary. The file '.hg/localtags' is used for local tags (not
2637 2640 shared among repositories).
2638 2641 """
2639 2642 if name in ['tip', '.']:
2640 2643 raise util.Abort(_("the name '%s' is reserved") % name)
2641 2644 if rev_ is not None:
2642 2645 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2643 2646 "please use 'hg tag [-r REV] NAME' instead\n"))
2644 2647 if opts['rev']:
2645 2648 raise util.Abort(_("use only one form to specify the revision"))
2646 2649 if opts['rev']:
2647 2650 rev_ = opts['rev']
2648 2651 if not rev_ and repo.dirstate.parents()[1] != nullid:
2649 2652 raise util.Abort(_('uncommitted merge - please provide a '
2650 2653 'specific revision'))
2651 2654 r = repo.changectx(rev_).node()
2652 2655
2653 2656 message = opts['message']
2654 2657 if not message:
2655 2658 message = _('Added tag %s for changeset %s') % (name, short(r))
2656 2659
2657 2660 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2658 2661
2659 2662 def tags(ui, repo):
2660 2663 """list repository tags
2661 2664
2662 2665 List the repository tags.
2663 2666
2664 2667 This lists both regular and local tags.
2665 2668 """
2666 2669
2667 2670 l = repo.tagslist()
2668 2671 l.reverse()
2669 2672 hexfunc = ui.debugflag and hex or short
2670 2673 for t, n in l:
2671 2674 try:
2672 2675 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2673 2676 except KeyError:
2674 2677 r = " ?:?"
2675 2678 if ui.quiet:
2676 2679 ui.write("%s\n" % t)
2677 2680 else:
2678 2681 ui.write("%-30s %s\n" % (t, r))
2679 2682
2680 2683 def tip(ui, repo, **opts):
2681 2684 """show the tip revision
2682 2685
2683 2686 Show the tip revision.
2684 2687 """
2685 2688 n = repo.changelog.tip()
2686 2689 br = None
2687 2690 if opts['branches']:
2688 2691 ui.warn(_("the --branches option is deprecated, "
2689 2692 "please use 'hg branches' instead\n"))
2690 2693 br = repo.branchlookup([n])
2691 2694 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2692 2695 if opts['patch']:
2693 2696 patch.diff(repo, repo.changelog.parents(n)[0], n)
2694 2697
2695 2698 def unbundle(ui, repo, fname, **opts):
2696 2699 """apply a changegroup file
2697 2700
2698 2701 Apply a compressed changegroup file generated by the bundle
2699 2702 command.
2700 2703 """
2701 2704 f = urllib.urlopen(fname)
2702 2705
2703 2706 header = f.read(6)
2704 2707 if not header.startswith("HG"):
2705 2708 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2706 2709 elif not header.startswith("HG10"):
2707 2710 raise util.Abort(_("%s: unknown bundle version") % fname)
2708 2711 elif header == "HG10BZ":
2709 2712 def generator(f):
2710 2713 zd = bz2.BZ2Decompressor()
2711 2714 zd.decompress("BZ")
2712 2715 for chunk in f:
2713 2716 yield zd.decompress(chunk)
2714 2717 elif header == "HG10UN":
2715 2718 def generator(f):
2716 2719 for chunk in f:
2717 2720 yield chunk
2718 2721 else:
2719 2722 raise util.Abort(_("%s: unknown bundle compression type")
2720 2723 % fname)
2721 2724 gen = generator(util.filechunkiter(f, 4096))
2722 2725 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle',
2723 2726 'bundle:' + fname)
2724 2727 return postincoming(ui, repo, modheads, opts['update'])
2725 2728
2726 2729 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2727 2730 branch=None):
2728 2731 """update or merge working directory
2729 2732
2730 2733 Update the working directory to the specified revision.
2731 2734
2732 2735 If there are no outstanding changes in the working directory and
2733 2736 there is a linear relationship between the current version and the
2734 2737 requested version, the result is the requested version.
2735 2738
2736 2739 To merge the working directory with another revision, use the
2737 2740 merge command.
2738 2741
2739 2742 By default, update will refuse to run if doing so would require
2740 2743 merging or discarding local changes.
2741 2744 """
2742 2745 node = _lookup(repo, node, branch)
2743 2746 if clean:
2744 2747 return hg.clean(repo, node)
2745 2748 else:
2746 2749 return hg.update(repo, node)
2747 2750
2748 2751 def _lookup(repo, node, branch=None):
2749 2752 if branch:
2750 2753 repo.ui.warn(_("the --branch option is deprecated, "
2751 2754 "please use 'hg branch' instead\n"))
2752 2755 br = repo.branchlookup(branch=branch)
2753 2756 found = []
2754 2757 for x in br:
2755 2758 if branch in br[x]:
2756 2759 found.append(x)
2757 2760 if len(found) > 1:
2758 2761 repo.ui.warn(_("Found multiple heads for %s\n") % branch)
2759 2762 for x in found:
2760 2763 show_changeset(ui, repo, {}).show(changenode=x, brinfo=br)
2761 2764 raise util.Abort("")
2762 2765 if len(found) == 1:
2763 2766 node = found[0]
2764 2767 repo.ui.warn(_("Using head %s for branch %s\n")
2765 2768 % (short(node), branch))
2766 2769 else:
2767 2770 raise util.Abort(_("branch %s not found") % branch)
2768 2771 else:
2769 2772 node = node and repo.lookup(node) or repo.changelog.tip()
2770 2773 return node
2771 2774
2772 2775 def verify(ui, repo):
2773 2776 """verify the integrity of the repository
2774 2777
2775 2778 Verify the integrity of the current repository.
2776 2779
2777 2780 This will perform an extensive check of the repository's
2778 2781 integrity, validating the hashes and checksums of each entry in
2779 2782 the changelog, manifest, and tracked files, as well as the
2780 2783 integrity of their crosslinks and indices.
2781 2784 """
2782 2785 return hg.verify(repo)
2783 2786
2784 2787 # Command options and aliases are listed here, alphabetically
2785 2788
2786 2789 globalopts = [
2787 2790 ('R', 'repository', '',
2788 2791 _('repository root directory or symbolic path name')),
2789 2792 ('', 'cwd', '', _('change working directory')),
2790 2793 ('y', 'noninteractive', None,
2791 2794 _('do not prompt, assume \'yes\' for any required answers')),
2792 2795 ('q', 'quiet', None, _('suppress output')),
2793 2796 ('v', 'verbose', None, _('enable additional output')),
2794 2797 ('', 'config', [], _('set/override config option')),
2795 2798 ('', 'debug', None, _('enable debugging output')),
2796 2799 ('', 'debugger', None, _('start debugger')),
2797 2800 ('', 'lsprof', None, _('print improved command execution profile')),
2798 2801 ('', 'traceback', None, _('print traceback on exception')),
2799 2802 ('', 'time', None, _('time how long the command takes')),
2800 2803 ('', 'profile', None, _('print command execution profile')),
2801 2804 ('', 'version', None, _('output version information and exit')),
2802 2805 ('h', 'help', None, _('display help and exit')),
2803 2806 ]
2804 2807
2805 2808 dryrunopts = [('n', 'dry-run', None,
2806 2809 _('do not perform actions, just print output'))]
2807 2810
2808 2811 remoteopts = [
2809 2812 ('e', 'ssh', '', _('specify ssh command to use')),
2810 2813 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2811 2814 ]
2812 2815
2813 2816 walkopts = [
2814 2817 ('I', 'include', [], _('include names matching the given patterns')),
2815 2818 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2816 2819 ]
2817 2820
2818 2821 table = {
2819 2822 "^add":
2820 2823 (add,
2821 2824 walkopts + dryrunopts,
2822 2825 _('hg add [OPTION]... [FILE]...')),
2823 2826 "addremove":
2824 2827 (addremove,
2825 2828 [('s', 'similarity', '',
2826 2829 _('guess renamed files by similarity (0<=s<=100)')),
2827 2830 ] + walkopts + dryrunopts,
2828 2831 _('hg addremove [OPTION]... [FILE]...')),
2829 2832 "^annotate":
2830 2833 (annotate,
2831 2834 [('r', 'rev', '', _('annotate the specified revision')),
2832 2835 ('f', 'follow', None, _('follow file copies and renames')),
2833 2836 ('a', 'text', None, _('treat all files as text')),
2834 2837 ('u', 'user', None, _('list the author')),
2835 2838 ('d', 'date', None, _('list the date')),
2836 2839 ('n', 'number', None, _('list the revision number (default)')),
2837 2840 ('c', 'changeset', None, _('list the changeset')),
2838 2841 ] + walkopts,
2839 2842 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2840 2843 "archive":
2841 2844 (archive,
2842 2845 [('', 'no-decode', None, _('do not pass files through decoders')),
2843 2846 ('p', 'prefix', '', _('directory prefix for files in archive')),
2844 2847 ('r', 'rev', '', _('revision to distribute')),
2845 2848 ('t', 'type', '', _('type of distribution to create')),
2846 2849 ] + walkopts,
2847 2850 _('hg archive [OPTION]... DEST')),
2848 2851 "backout":
2849 2852 (backout,
2850 2853 [('', 'merge', None,
2851 2854 _('merge with old dirstate parent after backout')),
2852 2855 ('m', 'message', '', _('use <text> as commit message')),
2853 2856 ('l', 'logfile', '', _('read commit message from <file>')),
2854 2857 ('d', 'date', '', _('record datecode as commit date')),
2855 2858 ('', 'parent', '', _('parent to choose when backing out merge')),
2856 2859 ('u', 'user', '', _('record user as committer')),
2857 2860 ] + walkopts,
2858 2861 _('hg backout [OPTION]... REV')),
2859 2862 "branch": (branch, [], _('hg branch [NAME]')),
2860 2863 "branches": (branches, [], _('hg branches')),
2861 2864 "bundle":
2862 2865 (bundle,
2863 2866 [('f', 'force', None,
2864 2867 _('run even when remote repository is unrelated')),
2865 2868 ('r', 'rev', [],
2866 2869 _('a changeset you would like to bundle')),
2867 2870 ('', 'base', [],
2868 2871 _('a base changeset to specify instead of a destination')),
2869 2872 ] + remoteopts,
2870 2873 _('hg bundle [--base REV]... [--rev REV]... FILE [DEST]')),
2871 2874 "cat":
2872 2875 (cat,
2873 2876 [('o', 'output', '', _('print output to file with formatted name')),
2874 2877 ('r', 'rev', '', _('print the given revision')),
2875 2878 ] + walkopts,
2876 2879 _('hg cat [OPTION]... FILE...')),
2877 2880 "^clone":
2878 2881 (clone,
2879 2882 [('U', 'noupdate', None, _('do not update the new working directory')),
2880 2883 ('r', 'rev', [],
2881 2884 _('a changeset you would like to have after cloning')),
2882 2885 ('', 'pull', None, _('use pull protocol to copy metadata')),
2883 2886 ('', 'uncompressed', None,
2884 2887 _('use uncompressed transfer (fast over LAN)')),
2885 2888 ] + remoteopts,
2886 2889 _('hg clone [OPTION]... SOURCE [DEST]')),
2887 2890 "^commit|ci":
2888 2891 (commit,
2889 2892 [('A', 'addremove', None,
2890 2893 _('mark new/missing files as added/removed before committing')),
2891 2894 ('m', 'message', '', _('use <text> as commit message')),
2892 2895 ('l', 'logfile', '', _('read the commit message from <file>')),
2893 2896 ('d', 'date', '', _('record datecode as commit date')),
2894 2897 ('u', 'user', '', _('record user as commiter')),
2895 2898 ] + walkopts,
2896 2899 _('hg commit [OPTION]... [FILE]...')),
2897 2900 "copy|cp":
2898 2901 (copy,
2899 2902 [('A', 'after', None, _('record a copy that has already occurred')),
2900 2903 ('f', 'force', None,
2901 2904 _('forcibly copy over an existing managed file')),
2902 2905 ] + walkopts + dryrunopts,
2903 2906 _('hg copy [OPTION]... [SOURCE]... DEST')),
2904 2907 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2905 2908 "debugcomplete":
2906 2909 (debugcomplete,
2907 2910 [('o', 'options', None, _('show the command options'))],
2908 2911 _('debugcomplete [-o] CMD')),
2909 2912 "debugrebuildstate":
2910 2913 (debugrebuildstate,
2911 2914 [('r', 'rev', '', _('revision to rebuild to'))],
2912 2915 _('debugrebuildstate [-r REV] [REV]')),
2913 2916 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2914 2917 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2915 2918 "debugstate": (debugstate, [], _('debugstate')),
2916 2919 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2917 2920 "debugindex": (debugindex, [], _('debugindex FILE')),
2918 2921 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2919 2922 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2920 2923 "debugwalk":
2921 2924 (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2922 2925 "^diff":
2923 2926 (diff,
2924 2927 [('r', 'rev', [], _('revision')),
2925 2928 ('a', 'text', None, _('treat all files as text')),
2926 2929 ('p', 'show-function', None,
2927 2930 _('show which function each change is in')),
2928 2931 ('g', 'git', None, _('use git extended diff format')),
2929 2932 ('', 'nodates', None, _("don't include dates in diff headers")),
2930 2933 ('w', 'ignore-all-space', None,
2931 2934 _('ignore white space when comparing lines')),
2932 2935 ('b', 'ignore-space-change', None,
2933 2936 _('ignore changes in the amount of white space')),
2934 2937 ('B', 'ignore-blank-lines', None,
2935 2938 _('ignore changes whose lines are all blank')),
2936 2939 ] + walkopts,
2937 2940 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2938 2941 "^export":
2939 2942 (export,
2940 2943 [('o', 'output', '', _('print output to file with formatted name')),
2941 2944 ('a', 'text', None, _('treat all files as text')),
2942 2945 ('g', 'git', None, _('use git extended diff format')),
2943 2946 ('', 'nodates', None, _("don't include dates in diff headers")),
2944 2947 ('', 'switch-parent', None, _('diff against the second parent'))],
2945 2948 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2946 2949 "grep":
2947 2950 (grep,
2948 2951 [('0', 'print0', None, _('end fields with NUL')),
2949 2952 ('', 'all', None, _('print all revisions that match')),
2950 2953 ('f', 'follow', None,
2951 2954 _('follow changeset history, or file history across copies and renames')),
2952 2955 ('i', 'ignore-case', None, _('ignore case when matching')),
2953 2956 ('l', 'files-with-matches', None,
2954 2957 _('print only filenames and revs that match')),
2955 2958 ('n', 'line-number', None, _('print matching line numbers')),
2956 2959 ('r', 'rev', [], _('search in given revision range')),
2957 2960 ('u', 'user', None, _('print user who committed change')),
2958 2961 ] + walkopts,
2959 2962 _('hg grep [OPTION]... PATTERN [FILE]...')),
2960 2963 "heads":
2961 2964 (heads,
2962 2965 [('b', 'branches', None, _('show branches (DEPRECATED)')),
2963 2966 ('', 'style', '', _('display using template map file')),
2964 2967 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2965 2968 ('', 'template', '', _('display with template'))],
2966 2969 _('hg heads [-r REV]')),
2967 2970 "help": (help_, [], _('hg help [COMMAND]')),
2968 2971 "identify|id": (identify, [], _('hg identify')),
2969 2972 "import|patch":
2970 2973 (import_,
2971 2974 [('p', 'strip', 1,
2972 2975 _('directory strip option for patch. This has the same\n'
2973 2976 'meaning as the corresponding patch option')),
2974 2977 ('m', 'message', '', _('use <text> as commit message')),
2975 2978 ('b', 'base', '', _('base path (DEPRECATED)')),
2976 2979 ('f', 'force', None,
2977 2980 _('skip check for outstanding uncommitted changes'))],
2978 2981 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2979 2982 "incoming|in": (incoming,
2980 2983 [('M', 'no-merges', None, _('do not show merges')),
2981 2984 ('f', 'force', None,
2982 2985 _('run even when remote repository is unrelated')),
2983 2986 ('', 'style', '', _('display using template map file')),
2984 2987 ('n', 'newest-first', None, _('show newest record first')),
2985 2988 ('', 'bundle', '', _('file to store the bundles into')),
2986 2989 ('p', 'patch', None, _('show patch')),
2987 2990 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2988 2991 ('', 'template', '', _('display with template')),
2989 2992 ] + remoteopts,
2990 2993 _('hg incoming [-p] [-n] [-M] [-r REV]...'
2991 2994 ' [--bundle FILENAME] [SOURCE]')),
2992 2995 "^init":
2993 2996 (init, remoteopts, _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
2994 2997 "locate":
2995 2998 (locate,
2996 2999 [('r', 'rev', '', _('search the repository as it stood at rev')),
2997 3000 ('0', 'print0', None,
2998 3001 _('end filenames with NUL, for use with xargs')),
2999 3002 ('f', 'fullpath', None,
3000 3003 _('print complete paths from the filesystem root')),
3001 3004 ] + walkopts,
3002 3005 _('hg locate [OPTION]... [PATTERN]...')),
3003 3006 "^log|history":
3004 3007 (log,
3005 3008 [('b', 'branches', None, _('show branches (DEPRECATED)')),
3006 3009 ('f', 'follow', None,
3007 3010 _('follow changeset history, or file history across copies and renames')),
3008 3011 ('', 'follow-first', None,
3009 3012 _('only follow the first parent of merge changesets')),
3010 3013 ('C', 'copies', None, _('show copied files')),
3011 3014 ('k', 'keyword', [], _('search for a keyword')),
3012 3015 ('l', 'limit', '', _('limit number of changes displayed')),
3013 3016 ('r', 'rev', [], _('show the specified revision or range')),
3014 3017 ('M', 'no-merges', None, _('do not show merges')),
3015 3018 ('', 'style', '', _('display using template map file')),
3016 3019 ('m', 'only-merges', None, _('show only merges')),
3017 3020 ('p', 'patch', None, _('show patch')),
3018 3021 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3019 3022 ('', 'template', '', _('display with template')),
3020 3023 ] + walkopts,
3021 3024 _('hg log [OPTION]... [FILE]')),
3022 3025 "manifest": (manifest, [], _('hg manifest [REV]')),
3023 3026 "merge":
3024 3027 (merge,
3025 3028 [('b', 'branch', '', _('merge with head of a specific branch (DEPRECATED)')),
3026 3029 ('f', 'force', None, _('force a merge with outstanding changes'))],
3027 3030 _('hg merge [-f] [REV]')),
3028 3031 "outgoing|out": (outgoing,
3029 3032 [('M', 'no-merges', None, _('do not show merges')),
3030 3033 ('f', 'force', None,
3031 3034 _('run even when remote repository is unrelated')),
3032 3035 ('p', 'patch', None, _('show patch')),
3033 3036 ('', 'style', '', _('display using template map file')),
3034 3037 ('r', 'rev', [], _('a specific revision you would like to push')),
3035 3038 ('n', 'newest-first', None, _('show newest record first')),
3036 3039 ('', 'template', '', _('display with template')),
3037 3040 ] + remoteopts,
3038 3041 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
3039 3042 "^parents":
3040 3043 (parents,
3041 3044 [('b', 'branches', None, _('show branches (DEPRECATED)')),
3042 3045 ('r', 'rev', '', _('show parents from the specified rev')),
3043 3046 ('', 'style', '', _('display using template map file')),
3044 3047 ('', 'template', '', _('display with template'))],
3045 3048 _('hg parents [-r REV] [FILE]')),
3046 3049 "paths": (paths, [], _('hg paths [NAME]')),
3047 3050 "^pull":
3048 3051 (pull,
3049 3052 [('u', 'update', None,
3050 3053 _('update to new tip if changesets were pulled')),
3051 3054 ('f', 'force', None,
3052 3055 _('run even when remote repository is unrelated')),
3053 3056 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
3054 3057 ] + remoteopts,
3055 3058 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
3056 3059 "^push":
3057 3060 (push,
3058 3061 [('f', 'force', None, _('force push')),
3059 3062 ('r', 'rev', [], _('a specific revision you would like to push')),
3060 3063 ] + remoteopts,
3061 3064 _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
3062 3065 "debugrawcommit|rawcommit":
3063 3066 (rawcommit,
3064 3067 [('p', 'parent', [], _('parent')),
3065 3068 ('d', 'date', '', _('date code')),
3066 3069 ('u', 'user', '', _('user')),
3067 3070 ('F', 'files', '', _('file list')),
3068 3071 ('m', 'message', '', _('commit message')),
3069 3072 ('l', 'logfile', '', _('commit message file'))],
3070 3073 _('hg debugrawcommit [OPTION]... [FILE]...')),
3071 3074 "recover": (recover, [], _('hg recover')),
3072 3075 "^remove|rm":
3073 3076 (remove,
3074 3077 [('A', 'after', None, _('record remove that has already occurred')),
3075 3078 ('f', 'force', None, _('remove file even if modified')),
3076 3079 ] + walkopts,
3077 3080 _('hg remove [OPTION]... FILE...')),
3078 3081 "rename|mv":
3079 3082 (rename,
3080 3083 [('A', 'after', None, _('record a rename that has already occurred')),
3081 3084 ('f', 'force', None,
3082 3085 _('forcibly copy over an existing managed file')),
3083 3086 ] + walkopts + dryrunopts,
3084 3087 _('hg rename [OPTION]... SOURCE... DEST')),
3085 3088 "^revert":
3086 3089 (revert,
3087 3090 [('a', 'all', None, _('revert all changes when no arguments given')),
3088 3091 ('r', 'rev', '', _('revision to revert to')),
3089 3092 ('', 'no-backup', None, _('do not save backup copies of files')),
3090 3093 ] + walkopts + dryrunopts,
3091 3094 _('hg revert [-r REV] [NAME]...')),
3092 3095 "rollback": (rollback, [], _('hg rollback')),
3093 3096 "root": (root, [], _('hg root')),
3094 3097 "showconfig|debugconfig":
3095 3098 (showconfig,
3096 3099 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3097 3100 _('showconfig [-u] [NAME]...')),
3098 3101 "^serve":
3099 3102 (serve,
3100 3103 [('A', 'accesslog', '', _('name of access log file to write to')),
3101 3104 ('d', 'daemon', None, _('run server in background')),
3102 3105 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3103 3106 ('E', 'errorlog', '', _('name of error log file to write to')),
3104 3107 ('p', 'port', 0, _('port to use (default: 8000)')),
3105 3108 ('a', 'address', '', _('address to use')),
3106 3109 ('n', 'name', '',
3107 3110 _('name to show in web pages (default: working dir)')),
3108 3111 ('', 'webdir-conf', '', _('name of the webdir config file'
3109 3112 ' (serve more than one repo)')),
3110 3113 ('', 'pid-file', '', _('name of file to write process ID to')),
3111 3114 ('', 'stdio', None, _('for remote clients')),
3112 3115 ('t', 'templates', '', _('web templates to use')),
3113 3116 ('', 'style', '', _('template style to use')),
3114 3117 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3115 3118 _('hg serve [OPTION]...')),
3116 3119 "^status|st":
3117 3120 (status,
3118 3121 [('A', 'all', None, _('show status of all files')),
3119 3122 ('m', 'modified', None, _('show only modified files')),
3120 3123 ('a', 'added', None, _('show only added files')),
3121 3124 ('r', 'removed', None, _('show only removed files')),
3122 3125 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3123 3126 ('c', 'clean', None, _('show only files without changes')),
3124 3127 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3125 3128 ('i', 'ignored', None, _('show ignored files')),
3126 3129 ('n', 'no-status', None, _('hide status prefix')),
3127 3130 ('C', 'copies', None, _('show source of copied files')),
3128 3131 ('0', 'print0', None,
3129 3132 _('end filenames with NUL, for use with xargs')),
3130 3133 ('', 'rev', [], _('show difference from revision')),
3131 3134 ] + walkopts,
3132 3135 _('hg status [OPTION]... [FILE]...')),
3133 3136 "tag":
3134 3137 (tag,
3135 3138 [('l', 'local', None, _('make the tag local')),
3136 3139 ('m', 'message', '', _('message for tag commit log entry')),
3137 3140 ('d', 'date', '', _('record datecode as commit date')),
3138 3141 ('u', 'user', '', _('record user as commiter')),
3139 3142 ('r', 'rev', '', _('revision to tag'))],
3140 3143 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3141 3144 "tags": (tags, [], _('hg tags')),
3142 3145 "tip":
3143 3146 (tip,
3144 3147 [('b', 'branches', None, _('show branches (DEPRECATED)')),
3145 3148 ('', 'style', '', _('display using template map file')),
3146 3149 ('p', 'patch', None, _('show patch')),
3147 3150 ('', 'template', '', _('display with template'))],
3148 3151 _('hg tip [-p]')),
3149 3152 "unbundle":
3150 3153 (unbundle,
3151 3154 [('u', 'update', None,
3152 3155 _('update to new tip if changesets were unbundled'))],
3153 3156 _('hg unbundle [-u] FILE')),
3154 3157 "^update|up|checkout|co":
3155 3158 (update,
3156 3159 [('b', 'branch', '',
3157 3160 _('checkout the head of a specific branch (DEPRECATED)')),
3158 3161 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3159 3162 ('C', 'clean', None, _('overwrite locally modified files')),
3160 3163 ('f', 'force', None, _('force a merge with outstanding changes'))],
3161 3164 _('hg update [-C] [-f] [REV]')),
3162 3165 "verify": (verify, [], _('hg verify')),
3163 3166 "version": (show_version, [], _('hg version')),
3164 3167 }
3165 3168
3166 3169 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3167 3170 " debugindex debugindexdot")
3168 3171 optionalrepo = ("paths serve showconfig")
3169 3172
3170 3173 def findpossible(ui, cmd):
3171 3174 """
3172 3175 Return cmd -> (aliases, command table entry)
3173 3176 for each matching command.
3174 3177 Return debug commands (or their aliases) only if no normal command matches.
3175 3178 """
3176 3179 choice = {}
3177 3180 debugchoice = {}
3178 3181 for e in table.keys():
3179 3182 aliases = e.lstrip("^").split("|")
3180 3183 found = None
3181 3184 if cmd in aliases:
3182 3185 found = cmd
3183 3186 elif not ui.config("ui", "strict"):
3184 3187 for a in aliases:
3185 3188 if a.startswith(cmd):
3186 3189 found = a
3187 3190 break
3188 3191 if found is not None:
3189 3192 if aliases[0].startswith("debug") or found.startswith("debug"):
3190 3193 debugchoice[found] = (aliases, table[e])
3191 3194 else:
3192 3195 choice[found] = (aliases, table[e])
3193 3196
3194 3197 if not choice and debugchoice:
3195 3198 choice = debugchoice
3196 3199
3197 3200 return choice
3198 3201
3199 3202 def findcmd(ui, cmd):
3200 3203 """Return (aliases, command table entry) for command string."""
3201 3204 choice = findpossible(ui, cmd)
3202 3205
3203 3206 if choice.has_key(cmd):
3204 3207 return choice[cmd]
3205 3208
3206 3209 if len(choice) > 1:
3207 3210 clist = choice.keys()
3208 3211 clist.sort()
3209 3212 raise AmbiguousCommand(cmd, clist)
3210 3213
3211 3214 if choice:
3212 3215 return choice.values()[0]
3213 3216
3214 3217 raise UnknownCommand(cmd)
3215 3218
3216 3219 def catchterm(*args):
3217 3220 raise util.SignalInterrupt
3218 3221
3219 3222 def run():
3220 3223 sys.exit(dispatch(sys.argv[1:]))
3221 3224
3222 3225 class ParseError(Exception):
3223 3226 """Exception raised on errors in parsing the command line."""
3224 3227
3225 3228 def parse(ui, args):
3226 3229 options = {}
3227 3230 cmdoptions = {}
3228 3231
3229 3232 try:
3230 3233 args = fancyopts.fancyopts(args, globalopts, options)
3231 3234 except fancyopts.getopt.GetoptError, inst:
3232 3235 raise ParseError(None, inst)
3233 3236
3234 3237 if args:
3235 3238 cmd, args = args[0], args[1:]
3236 3239 aliases, i = findcmd(ui, cmd)
3237 3240 cmd = aliases[0]
3238 3241 defaults = ui.config("defaults", cmd)
3239 3242 if defaults:
3240 3243 args = shlex.split(defaults) + args
3241 3244 c = list(i[1])
3242 3245 else:
3243 3246 cmd = None
3244 3247 c = []
3245 3248
3246 3249 # combine global options into local
3247 3250 for o in globalopts:
3248 3251 c.append((o[0], o[1], options[o[1]], o[3]))
3249 3252
3250 3253 try:
3251 3254 args = fancyopts.fancyopts(args, c, cmdoptions)
3252 3255 except fancyopts.getopt.GetoptError, inst:
3253 3256 raise ParseError(cmd, inst)
3254 3257
3255 3258 # separate global options back out
3256 3259 for o in globalopts:
3257 3260 n = o[1]
3258 3261 options[n] = cmdoptions[n]
3259 3262 del cmdoptions[n]
3260 3263
3261 3264 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3262 3265
3263 3266 external = {}
3264 3267
3265 3268 def findext(name):
3266 3269 '''return module with given extension name'''
3267 3270 try:
3268 3271 return sys.modules[external[name]]
3269 3272 except KeyError:
3270 3273 for k, v in external.iteritems():
3271 3274 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3272 3275 return sys.modules[v]
3273 3276 raise KeyError(name)
3274 3277
3275 3278 def load_extensions(ui):
3276 3279 added = []
3277 3280 for ext_name, load_from_name in ui.extensions():
3278 3281 if ext_name in external:
3279 3282 continue
3280 3283 try:
3281 3284 if load_from_name:
3282 3285 # the module will be loaded in sys.modules
3283 3286 # choose an unique name so that it doesn't
3284 3287 # conflicts with other modules
3285 3288 module_name = "hgext_%s" % ext_name.replace('.', '_')
3286 3289 mod = imp.load_source(module_name, load_from_name)
3287 3290 else:
3288 3291 def importh(name):
3289 3292 mod = __import__(name)
3290 3293 components = name.split('.')
3291 3294 for comp in components[1:]:
3292 3295 mod = getattr(mod, comp)
3293 3296 return mod
3294 3297 try:
3295 3298 mod = importh("hgext.%s" % ext_name)
3296 3299 except ImportError:
3297 3300 mod = importh(ext_name)
3298 3301 external[ext_name] = mod.__name__
3299 3302 added.append((mod, ext_name))
3300 3303 except (util.SignalInterrupt, KeyboardInterrupt):
3301 3304 raise
3302 3305 except Exception, inst:
3303 3306 ui.warn(_("*** failed to import extension %s: %s\n") %
3304 3307 (ext_name, inst))
3305 3308 if ui.print_exc():
3306 3309 return 1
3307 3310
3308 3311 for mod, name in added:
3309 3312 uisetup = getattr(mod, 'uisetup', None)
3310 3313 if uisetup:
3311 3314 uisetup(ui)
3312 3315 cmdtable = getattr(mod, 'cmdtable', {})
3313 3316 for t in cmdtable:
3314 3317 if t in table:
3315 3318 ui.warn(_("module %s overrides %s\n") % (name, t))
3316 3319 table.update(cmdtable)
3317 3320
3318 3321 def parseconfig(config):
3319 3322 """parse the --config options from the command line"""
3320 3323 parsed = []
3321 3324 for cfg in config:
3322 3325 try:
3323 3326 name, value = cfg.split('=', 1)
3324 3327 section, name = name.split('.', 1)
3325 3328 if not section or not name:
3326 3329 raise IndexError
3327 3330 parsed.append((section, name, value))
3328 3331 except (IndexError, ValueError):
3329 3332 raise util.Abort(_('malformed --config option: %s') % cfg)
3330 3333 return parsed
3331 3334
3332 3335 def dispatch(args):
3333 3336 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3334 3337 num = getattr(signal, name, None)
3335 3338 if num: signal.signal(num, catchterm)
3336 3339
3337 3340 try:
3338 3341 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3339 3342 except util.Abort, inst:
3340 3343 sys.stderr.write(_("abort: %s\n") % inst)
3341 3344 return -1
3342 3345
3343 3346 load_extensions(u)
3344 3347 u.addreadhook(load_extensions)
3345 3348
3346 3349 try:
3347 3350 cmd, func, args, options, cmdoptions = parse(u, args)
3348 3351 if options["time"]:
3349 3352 def get_times():
3350 3353 t = os.times()
3351 3354 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3352 3355 t = (t[0], t[1], t[2], t[3], time.clock())
3353 3356 return t
3354 3357 s = get_times()
3355 3358 def print_time():
3356 3359 t = get_times()
3357 3360 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3358 3361 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3359 3362 atexit.register(print_time)
3360 3363
3361 3364 # enter the debugger before command execution
3362 3365 if options['debugger']:
3363 3366 pdb.set_trace()
3364 3367
3365 3368 try:
3366 3369 if options['cwd']:
3367 3370 try:
3368 3371 os.chdir(options['cwd'])
3369 3372 except OSError, inst:
3370 3373 raise util.Abort('%s: %s' %
3371 3374 (options['cwd'], inst.strerror))
3372 3375
3373 3376 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3374 3377 not options["noninteractive"], options["traceback"],
3375 3378 parseconfig(options["config"]))
3376 3379
3377 3380 path = u.expandpath(options["repository"]) or ""
3378 3381 repo = path and hg.repository(u, path=path) or None
3379 3382 if repo and not repo.local():
3380 3383 raise util.Abort(_("repository '%s' is not local") % path)
3381 3384
3382 3385 if options['help']:
3383 3386 return help_(u, cmd, options['version'])
3384 3387 elif options['version']:
3385 3388 return show_version(u)
3386 3389 elif not cmd:
3387 3390 return help_(u, 'shortlist')
3388 3391
3389 3392 if cmd not in norepo.split():
3390 3393 try:
3391 3394 if not repo:
3392 3395 repo = hg.repository(u, path=path)
3393 3396 u = repo.ui
3394 3397 for name in external.itervalues():
3395 3398 mod = sys.modules[name]
3396 3399 if hasattr(mod, 'reposetup'):
3397 3400 mod.reposetup(u, repo)
3398 3401 hg.repo_setup_hooks.append(mod.reposetup)
3399 3402 except hg.RepoError:
3400 3403 if cmd not in optionalrepo.split():
3401 3404 raise
3402 3405 d = lambda: func(u, repo, *args, **cmdoptions)
3403 3406 else:
3404 3407 d = lambda: func(u, *args, **cmdoptions)
3405 3408
3406 3409 try:
3407 3410 if options['profile']:
3408 3411 import hotshot, hotshot.stats
3409 3412 prof = hotshot.Profile("hg.prof")
3410 3413 try:
3411 3414 try:
3412 3415 return prof.runcall(d)
3413 3416 except:
3414 3417 try:
3415 3418 u.warn(_('exception raised - generating '
3416 3419 'profile anyway\n'))
3417 3420 except:
3418 3421 pass
3419 3422 raise
3420 3423 finally:
3421 3424 prof.close()
3422 3425 stats = hotshot.stats.load("hg.prof")
3423 3426 stats.strip_dirs()
3424 3427 stats.sort_stats('time', 'calls')
3425 3428 stats.print_stats(40)
3426 3429 elif options['lsprof']:
3427 3430 try:
3428 3431 from mercurial import lsprof
3429 3432 except ImportError:
3430 3433 raise util.Abort(_(
3431 3434 'lsprof not available - install from '
3432 3435 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3433 3436 p = lsprof.Profiler()
3434 3437 p.enable(subcalls=True)
3435 3438 try:
3436 3439 return d()
3437 3440 finally:
3438 3441 p.disable()
3439 3442 stats = lsprof.Stats(p.getstats())
3440 3443 stats.sort()
3441 3444 stats.pprint(top=10, file=sys.stderr, climit=5)
3442 3445 else:
3443 3446 return d()
3444 3447 finally:
3445 3448 u.flush()
3446 3449 except:
3447 3450 # enter the debugger when we hit an exception
3448 3451 if options['debugger']:
3449 3452 pdb.post_mortem(sys.exc_info()[2])
3450 3453 u.print_exc()
3451 3454 raise
3452 3455 except ParseError, inst:
3453 3456 if inst.args[0]:
3454 3457 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3455 3458 help_(u, inst.args[0])
3456 3459 else:
3457 3460 u.warn(_("hg: %s\n") % inst.args[1])
3458 3461 help_(u, 'shortlist')
3459 3462 except AmbiguousCommand, inst:
3460 3463 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3461 3464 (inst.args[0], " ".join(inst.args[1])))
3462 3465 except UnknownCommand, inst:
3463 3466 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3464 3467 help_(u, 'shortlist')
3465 3468 except hg.RepoError, inst:
3466 3469 u.warn(_("abort: %s!\n") % inst)
3467 3470 except lock.LockHeld, inst:
3468 3471 if inst.errno == errno.ETIMEDOUT:
3469 3472 reason = _('timed out waiting for lock held by %s') % inst.locker
3470 3473 else:
3471 3474 reason = _('lock held by %s') % inst.locker
3472 3475 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3473 3476 except lock.LockUnavailable, inst:
3474 3477 u.warn(_("abort: could not lock %s: %s\n") %
3475 3478 (inst.desc or inst.filename, inst.strerror))
3476 3479 except revlog.RevlogError, inst:
3477 3480 u.warn(_("abort: %s!\n") % inst)
3478 3481 except util.SignalInterrupt:
3479 3482 u.warn(_("killed!\n"))
3480 3483 except KeyboardInterrupt:
3481 3484 try:
3482 3485 u.warn(_("interrupted!\n"))
3483 3486 except IOError, inst:
3484 3487 if inst.errno == errno.EPIPE:
3485 3488 if u.debugflag:
3486 3489 u.warn(_("\nbroken pipe\n"))
3487 3490 else:
3488 3491 raise
3489 3492 except IOError, inst:
3490 3493 if hasattr(inst, "code"):
3491 3494 u.warn(_("abort: %s\n") % inst)
3492 3495 elif hasattr(inst, "reason"):
3493 3496 u.warn(_("abort: error: %s\n") % inst.reason[1])
3494 3497 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3495 3498 if u.debugflag:
3496 3499 u.warn(_("broken pipe\n"))
3497 3500 elif getattr(inst, "strerror", None):
3498 3501 if getattr(inst, "filename", None):
3499 3502 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3500 3503 else:
3501 3504 u.warn(_("abort: %s\n") % inst.strerror)
3502 3505 else:
3503 3506 raise
3504 3507 except OSError, inst:
3505 3508 if getattr(inst, "filename", None):
3506 3509 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3507 3510 else:
3508 3511 u.warn(_("abort: %s\n") % inst.strerror)
3509 3512 except util.UnexpectedOutput, inst:
3510 3513 u.warn(_("abort: %s") % inst[0])
3511 3514 if not isinstance(inst[1], basestring):
3512 3515 u.warn(" %r\n" % (inst[1],))
3513 3516 elif not inst[1]:
3514 3517 u.warn(_(" empty string\n"))
3515 3518 else:
3516 3519 u.warn("\n%r%s\n" %
3517 3520 (inst[1][:400], len(inst[1]) > 400 and '...' or ''))
3518 3521 except util.Abort, inst:
3519 3522 u.warn(_("abort: %s\n") % inst)
3520 3523 except TypeError, inst:
3521 3524 # was this an argument error?
3522 3525 tb = traceback.extract_tb(sys.exc_info()[2])
3523 3526 if len(tb) > 2: # no
3524 3527 raise
3525 3528 u.debug(inst, "\n")
3526 3529 u.warn(_("%s: invalid arguments\n") % cmd)
3527 3530 help_(u, cmd)
3528 3531 except SystemExit, inst:
3529 3532 # Commands shouldn't sys.exit directly, but give a return code.
3530 3533 # Just in case catch this and and pass exit code to caller.
3531 3534 return inst.code
3532 3535 except:
3533 3536 u.warn(_("** unknown exception encountered, details follow\n"))
3534 3537 u.warn(_("** report bug details to "
3535 3538 "http://www.selenic.com/mercurial/bts\n"))
3536 3539 u.warn(_("** or mercurial@selenic.com\n"))
3537 3540 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3538 3541 % version.get_version())
3539 3542 raise
3540 3543
3541 3544 return -1
General Comments 0
You need to be logged in to leave comments. Login now